QStandardItemModel header with widget and text - qt

i need to use checkbox with text, like this "Check all":
in header of QStanndardItemModel. I tried like this
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
_model->setHorizontalHeaderItem(1, item0);
This way only works for items not for header, i mean for items if i use
_model->setItem(new QStandardItem(some_item);
I heard about writing my own class which inherit QHeaderView but i dont know if this can help in my problem. I would ask if there is a easy way to achieve this?
Regards

i did something like this:
QStandardItemModel *model = new QStandardItemModel(this);
ui->tableView->setModel(model);
QtCheckHeaderView *header = new QtCheckHeaderView(Qt::Horizontal, ui->tableView);
QtCheckHeaderView *vheader = new QtCheckHeaderView(Qt::Vertical, ui->tableView);
ui->tableView->setHorizontalHeader(header);
ui->tableView->setVerticalHeader(vheader);
QStandardItem *root = model->invisibleRootItem();
QList<QList<QStandardItem *> > items;
for (int i = 0; i < 10; ++i)
{
QList<QStandardItem *> res;
for (int j = 0; j < 1000; ++j)
{
res.append(new QStandardItem(QString("%1;%2").arg(j).arg(i)));
vheader->addCheckable(j);
}
items.append(res);
root->appendColumn(res);
header->addCheckable(i);
}
this works great. Draws checkbox with text in vertical and horizontal header.
But this works great only if i put this code to MainWindow constructor. If i put this code to for example pushbutton function this wont work. Datas are fine but headers are invisible.
ui->tableView->repaint();
wont work. Someone maybe knows the answer why this is happening and/or how to solve this?
thanks for answer

Qt models don't support item flags for headers. Setting item flags on items that are used as headers has no effect. QHeaderView doesn't support drawing checkboxes. I'm afraid subclassing QHeaderView is the simpliest way.
I wrote an example of how it can be implemented based on this FAQ page. This class assumes that you use QStandardItemModel and uses flags of its header items. If someone would want to use other model class, subclassing QAbstractItemModel and implementing missing interface and changing headerview implementation would be needed.
class MyHeader : public QHeaderView
{
public:
MyHeader(Qt::Orientation orientation, QWidget * parent = 0);
protected:
QStandardItemModel* standard_model;
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
virtual void mousePressEvent(QMouseEvent *event);
virtual void setModel(QAbstractItemModel* model);
};
MyHeader::MyHeader(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent)
{
standard_model = 0;
}
void MyHeader::paintSection(QPainter *painter, const QRect &rect, int logical_index) const {
painter->save();
QHeaderView::paintSection(painter, rect, logical_index);
painter->restore();
if (standard_model && orientation() == Qt::Horizontal) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
int pos = sectionViewportPosition(logical_index);
QStyleOptionButton option;
option.rect = QRect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (item->checkState() == Qt::Checked) {
option.state = QStyle::State_On;
} else {
option.state = QStyle::State_Off;
}
option.state |= QStyle::State_Enabled;
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter);
}
}
}
void MyHeader::mousePressEvent(QMouseEvent *event) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
if (standard_model && orientation() == Qt::Horizontal) {
for(int logical_index = 0; logical_index < count(); logical_index++) {
int pos = sectionViewportPosition(logical_index);
QRect rect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (rect.contains(event->pos())) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
return;
}
}
}
}
QHeaderView::mousePressEvent(event);
}
void MyHeader::setModel(QAbstractItemModel *model) {
QHeaderView::setModel(model);
standard_model = qobject_cast<QStandardItemModel*>(model);
}
//usage
QTableView view;
QStandardItemModel model;
model.setColumnCount(5);
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
model.setHorizontalHeaderItem(0, item0);
view.setModel(&model);
MyHeader *myHeader = new MyHeader(Qt::Horizontal, &view);
view.setHorizontalHeader(myHeader);
view.show();

Related

Is it possible to have a "wrap" behaviour in QMenu?

I have to store many items in a QMenu. If there is too many items QMenu wraps them ans begins a new column, but it happens only if these items can not fit into screen height.
I'd like to have QMenu which wraps items when the menu height reaches, for example, parent widget's height or any other custom value.
I wasn't able to find any properties in QMenu for achieving this. Setting maximumHeight gave no result. After digging into QMenu sources I found that the "wrapping logic" works based on popupGeometry method result. But popupGeometry uses screen size, and it is private so I don't know a way to change it.
As I didn't find the answer, I had to implement this control by myself.
It is a popup widget, using QToolButton as an owner. It can arrange items in a grid depending on item's height and required menu height.
class myLabel:public QLabel
{
Q_OBJECT
// QObject interface
public:
myLabel(QWidget* parent=0):QLabel(parent){}
bool event(QEvent *e)
{
if(e->type()==QEvent::MouseButtonPress)
emit clicked();
return QLabel::event(e);
}
void setAction(QAction *a)
{
setText(a->text());
_action=a;
}
QAction* action()
{
return _action;
}
signals:
void clicked();
private:
QAction* _action;
};
class myMenu: public QWidget
{
Q_OBJECT
public:
myMenu(QWidget* owner,QWidget* parent=0):QWidget(parent){
this->setWindowFlags(Qt::Popup);
l = new QGridLayout(this);
l->setContentsMargins(QMargins(3,3,3,3));
_owner=owner;
QString style="QLabel:hover{background-color: white;} ";
setStyleSheet(style);
}
void addAction(QAction*a){_actions.append(a);}
QVector<QAction*> actions(){return _actions;}
void setItemHeight(int val){_itemHeight=val;}
void setHeight(int val){_height=val;}
private:
QVector<QAction*> _actions;
QGridLayout *l ;
QWidget*_owner;
int _itemHeight=30;
int _height=200;
private slots:
void popup()
{
clear();
//move popup under toolbutton
QPoint p = _owner->geometry().bottomLeft();
p.setY(p.y()+1);
this->move(_owner->parentWidget()->mapToGlobal(p));
//calculate rows count
int rows = _height/_itemHeight;
//calculate cols count
int cols = _actions.size()/rows;
int d = _actions.size()%rows;
if(d>0)
cols++;
for(int i=0;i<rows;i++)
for(int j=0;j<cols;j++)
{
int index = i+j*rows;
if(index<_actions.size())
{
myLabel *lb = new myLabel(this);
connect(lb,SIGNAL(clicked()),this,SLOT(onClick()));
lb->setFixedHeight(_itemHeight);
lb->setAction(_actions[index]);
l->addWidget(lb,i,j);
}
}
this->repaint();
this->show();
}
void clear()
{
while(l->itemAt(0)!=NULL)
{
QLayoutItem* i = l->takeAt(0);
if(i->widget())
delete i->widget();
if(i->layout())
delete i->layout();
delete i;
}
}
void onClick()
{
myLabel *g = qobject_cast<myLabel*>(sender());
g->action()->trigger();
close();
}
// QWidget interface
protected:
void closeEvent(QCloseEvent *)
{
qobject_cast<QToolButton*>(_owner)->setDown(false);
}
signals:
void closed();
};
There's also an example showing how to create and fill myMenu and how to receive a selected action.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//set-up myMenu, btMyMenu is a QToolButton
myMenu *mMenu = new myMenu(ui->btMyMenu,this);
connect(ui->btMyMenu,SIGNAL(pressed()),mMenu,SLOT(popup()));
for(int i=0;i<20;i++)
{
QAction *a = new QAction("Action "+QString::number(i),this);
connect(a,SIGNAL(triggered(bool)),this,SLOT(onActSelected()));
mMenu->addAction(a);
}
//mMenu can be customized
mMenu->setHeight(100);
mMenu->setItemHeight(50);
}
void MainWindow::onActSelected()
{
QAction *a = qobject_cast<QAction*>(sender());
ui->btMyMenu->setText(a->text());
}
Any comments about how to improve this solution are appreciated!

How to select child's items checkBoxs in QTreeView when select their parent's checkbox

I want to select/unselect all child's items QCheckBoxs when I select/unselect their parent's item QCheckBox.
i inherit from QTreeView and detect when the QCheckBox is selected then i call function to do selecting/unselecting process.
here my code:
#ifndef MYQTREEVIEW_H
#define MYQTREEVIEW_H
#include <QTreeView>
#include <QMouseEvent>
#include <QDebug>
#include <QStandardItem>
class MyQTreeView: public QTreeView {
public:
MyQTreeView(QWidget* parent=0): QTreeView(parent){}
virtual ~MyQTreeView() {}
protected:
void resettingCheckBox (QModelIndex& parentIndex) {
if ( ! parentIndex.isValid() )
return;
QString text = parentIndex.data( Qt::DisplayRole ).value<QString>();
qDebug() << "parent is: " << text;
if ( model()->hasChildren(parentIndex) ) {
for( int i = 0; i < model()->rowCount(parentIndex) ; i++ ) {
QModelIndex childIndex = model()->index( i, 0, parentIndex );
if ( model()->hasChildren(childIndex) )
resettingCheckBox(childIndex);
else {
QString text = childIndex.data( Qt::DisplayRole ).value<QString>();
qDebug() << "child is: " << text;
QStandardItem *parentItem = static_cast<QStandardItem*> (parentIndex.internalPointer());
QStandardItem *childItem = static_cast<QStandardItem*> (childIndex.internalPointer());
if ( parentItem->checkState() == Qt::Checked ) {
qDebug() << "child item " << childItem->checkState();
childItem->setCheckState( Qt::Unchecked);
}
else {
qDebug() << "child item " << childItem->checkState();
childItem->setCheckState( Qt::Checked);
}
}
}
}
}
void mousePressEvent (QMouseEvent *event) {
QModelIndex index = indexAt(event->pos());
if(index.isValid()) {
QStyleOptionButton opt;
opt.QStyleOption::operator=(viewOptions());
opt.rect = visualRect(index);
QRect rect = style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt);
if (rect.contains(event->pos())) {
resettingCheckBox(index);
}
QTreeView::mousePressEvent(event);
}
}
};
#endif // MYQTREEVIEW_H
the code is not working probably when i select/unselect parent checkBox (subchilds is not selected/unselected).
Thanks in advance.
I believe the best way to manipulate treeview items is through the model. It looks like you're using QStandardItemModel; so you can override your model's setData method and reset child items values for the item index passed as a parameter to this method. Below is a small example:
class TestModel : public QStandardItemModel
{
public:
TestModel() {}
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)
{
QStandardItem *item = itemFromIndex(index);
for( int i = 0; i < item->rowCount() ; i++ )
{
QStandardItem *childItem = item->child(i);
setData(childItem->index(), value, role);
}
return QStandardItemModel::setData(index, value, role);
}
};
here's how this model gets initialized:
QStandardItemModel* tableModel = new TestModel();
QStandardItem* parentItem = tableModel->invisibleRootItem();
for (int i = 0; i < 4; ++i)
{
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
item->setCheckable(true);
parentItem->appendRow(item);
parentItem = item;
}
treeView->setModel(tableModel);
hope this helps, regards
It seems to me that you should call QTreeView::mousePressEvent(event) before resettingCheckBox(index), to let QTreeView update the checkState.
override mouseReleaseEvent() instead of mousePressEvent()!
because the checkState changes when mouse Release not mouse - press!

QTreeview mousePressEvent implementation prevents selection of items

Hello all
I have class that inherited from Qtreeview and I implement simple ( empty ) mousePressEvent function
But whenever I try to do this , the selection of the items in the Qtreeview are disabled , when I remove this function everything is working fine
What im missing here ?
Here Is the code:
void MyTreeWidget::mousePressEvent(QMouseEvent *event)
{
QModelIndex index = this->indexAt(event->pos());
QAbstractItemModel *model = this->model();
QMap<int, QVariant> ItemData = model->itemData(index);
QMap<int, QVariant>::const_iterator i = ItemData.constBegin();
while (i != ItemData.constEnd()) {
QString k = QString::number(i.key());
QString v = i.value().toString();
++i;
}
if (event->button() == Qt::LeftButton) {
QByteArray itemData ;
QString urlTo;
itemData.append(urlTo);
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
if (dropAction == Qt::MoveAction)
{
UT::getInstance()->LogToFile("dropAction");
}
}
QTreeView::mousePressEvent(event);
}
It's because that when you override a method, the original on is not called anymore. You would have to manually call the mousePressEvent method of QTreeView in the method you created.
Here is how to do it:
void YourClass::mousePressEvent ( QMouseEvent * event )
{
QTreeView::mousePressEvent(event);
}
Hope this helps.

HowTo restore QTreeView last expanded state?

What I have:
QTreeView class with table data
And connected QAbstractTableModel model
Question: how to save expanded state of items? Is some one have finished solutions?
PS: I know, that I can do this code by myself, but I don't have much time, and this is not the major problem of our project, but still we need it, because app contain a lot of such tables, and every time expanding tree items is annoyed process...
First, thanks to Razi for persistentIndexList and isExpanded way.
Second, here is the code which works for me just fine :-)
dialog.h file:
class Dialog : public QDialog
{
Q_OBJECT;
TreeModel *model;
TreeView *view;
public:
Dialog(QWidget *parent = 0);
~Dialog(void);
void reload(void);
protected:
void createGUI(void);
void closeEvent(QCloseEvent *);
void saveState(void);
void restoreState(void);
};
dialog.cpp file:
Dialog::Dialog(QWidget *parent)
{
createGUI();
reload();
}
Dialog::~Dialog(void) {};
void Dialog::reload(void)
{
restoreState();
}
void Dialog::createGUI(void)
{
QFile file(":/Resources/default.txt");
file.open(QIODevice::ReadOnly);
model = new TreeModel(file.readAll());
file.close();
view = new TreeView(this);
view->setModel(model);
QVBoxLayout *mainVLayout = new QVBoxLayout;
mainVLayout->addWidget(view);
setLayout(mainVLayout);
}
void Dialog::closeEvent(QCloseEvent *event_)
{
saveState();
}
void Dialog::saveState(void)
{
QStringList List;
// prepare list
// PS: getPersistentIndexList() function is a simple `return this->persistentIndexList()` from TreeModel model class
foreach (QModelIndex index, model->getPersistentIndexList())
{
if (view->isExpanded(index))
{
List << index.data(Qt::DisplayRole).toString();
}
}
// save list
QSettings settings("settings.ini", QSettings::IniFormat);
settings.beginGroup("MainWindow");
settings.setValue("ExpandedItems", QVariant::fromValue(List));
settings.endGroup();
}
void Dialog::restoreState(void)
{
QStringList List;
// get list
QSettings settings("settings.ini", QSettings::IniFormat);
settings.beginGroup("MainWindow");
List = settings.value("ExpandedItems").toStringList();
settings.endGroup();
foreach (QString item, List)
{
// search `item` text in model
QModelIndexList Items = model->match(model->index(0, 0), Qt::DisplayRole, QVariant::fromValue(item));
if (!Items.isEmpty())
{
// Information: with this code, expands ONLY first level in QTreeView
view->setExpanded(Items.first(), true);
}
}
}
Have a nice day!)
PS: this example based on C:\Qt\4.6.3\examples\itemviews\simpletreemodel code.
Thanks to Razi and mosg I was able to get this working. I made it restore the expanded state recursively so I thought I would share that part.
void applyExpandState_sub(QStringList& expandedItems,
QTreeView* treeView,
QAbstractItemModel* model,
QModelIndex startIndex)
{
foreach (QString item, expandedItems)
{
QModelIndexList matches = model->match( startIndex, Qt::UserRole, item );
foreach (QModelIndex index, matches)
{
treeView->setExpanded( index, true );
applyExpandState_sub(expandedItems,
treeView,
model,
model->index( 0, 0, index ) );
}
}
}
Then use like:
void myclass::applyExpandState()
{
m_treeView->setUpdatesEnabled(false);
applyExpandState_sub( m_expandedItems,
m_treeView,
m_model,
m_model->index( 0, 0, QModelIndex() ) );
m_treeView->setUpdatesEnabled(true);
}
I am using the Qt::UserRole here because multiple items in my model can have the same display name which would mess up the expand state restoration, so the UserRole provides a unique identifier for each item to avoid that problem.
These two function by using a loop should do that for you:
QModelIndexList QAbstractItemModel::persistentIndexList () const
bool isExpanded ( const QModelIndex & index ) const
Here is a general approach that should work with any QTreeView based widget, that uses some sort of ID system to identify elements (I am assuming the ID is an int, which is stored inside the Qt::UserRole):
void MyWidget::saveExpandedState()
{
for(int row = 0; row < tree_view_->model()->rowCount(); ++row)
saveExpandedOnLevel(tree_view_->model()->index(row,0));
}
void Widget::restoreExpandedState()
{
tree_view_->setUpdatesEnabled(false);
for(int row = 0; row < tree_view_->model()->rowCount(); ++row)
restoreExpandedOnLevel(tree_view_->model()->index(row,0));
tree_view_->setUpdatesEnabled(true);
}
void MyWidget::saveExpandedOnLevel(const QModelIndex& index)
{
if(tree_view_->isExpanded(index)) {
if(index.isValid())
expanded_ids_.insert(index.data(Qt::UserRole).toInt());
for(int row = 0; row < tree_view_->model()->rowCount(index); ++row)
saveExpandedOnLevel(index.child(row,0));
}
}
void MyWidget::restoreExpandedOnLevel(const QModelIndex& index)
{
if(expanded_ids_.contains(index.data(Qt::UserRole).toInt())) {
tree_view_->setExpanded(index, true);
for(int row = 0; row < tree_view_->model()->rowCount(index); ++row)
restoreExpandedOnLevel(index.child(row,0));
}
}
Instead of MyWidget::saveExpandedState() and MyWidget::saveExpandedState() one could also directly call MyWidget::saveExpandedOnLevel(tree_view_->rootIndex()) and MyWidget::restoreExpandedOnLevel(tree_view_->rootIndex()). I only used the above implementation because the for loop will be called anyway and MyWidget::saveExpandedState() and MyWidget::saveExpandedState() looked cleaner with my SIGNAL and SLOT design.
I have reworked iforce2d's solution into this:
void ApplyExpandState(QStringList & nodes,
QTreeView * view,
QAbstractItemModel * model,
const QModelIndex startIndex,
QString path)
{
path+=QString::number(startIndex.row()) + QString::number(startIndex.column());
for(int i(0); i < model->rowCount(startIndex); ++i)
{
QModelIndex nextIndex = model->index(i, 0, startIndex);
QString nextPath = path + QString::number(nextIndex.row()) + QString::number(nextIndex.column());
if(!nodes.contains(nextPath))
continue;
ApplyExpandState(nodes, view, model, model->index(i, 0, startIndex), path);
}
if(nodes.contains(path))
view->setExpanded( startIndex.sibling(startIndex.row(), 0), true );
}
void StoreExpandState(QStringList & nodes,
QTreeView * view,
QAbstractItemModel * model,
const QModelIndex startIndex,
QString path)
{
path+=QString::number(startIndex.row()) + QString::number(startIndex.column());
for(int i(0); i < model->rowCount(startIndex); ++i)
{
if(!view->isExpanded(model->index(i, 0, startIndex)))
continue;
StoreExpandState(nodes, view, model, model->index(i, 0, startIndex), path);
}
if(view->isExpanded(startIndex))
nodes << path;
}
This way there is no need to match data. Obviously - for this approach to work, tree needs to stay relatively unchanged. If you somehow change the order of tree items - it will expand wrong nodes.
Here is a version which doesn't rely on nodes having a unique Qt::UserRole or Qt::DisplayRole - it just serialises the entire QModelIndex
header:
#pragma once
#include <QTreeView>
class TreeView : public QTreeView
{
Q_OBJECT
public:
using QTreeView::QTreeView;
QStringList saveExpandedState(const QModelIndexList&) const;
void restoreExpandedState(const QStringList&);
};
source:
#include "tree_view.h"
#include <QAbstractItemModel>
namespace
{
std::string toString(const QModelIndex& index)
{
std::string parent = index.parent().isValid() ? toString(index.parent()) : "X";
char buf[512];
sprintf(buf, "%d:%d[%s]", index.row(), index.column(), parent.c_str());
return buf;
}
QModelIndex fromString(const std::string& string, QAbstractItemModel& model)
{
int row, column;
char parent_str[512];
sscanf(string.c_str(), "%d:%d[%s]", &row, &column, parent_str);
QModelIndex parent = *parent_str == 'X' ? QModelIndex() : fromString(parent_str, model);
return model.index(row, column, parent);
}
}
QStringList TreeView::saveExpandedState(const QModelIndexList& indices) const
{
QStringList list;
for (const QModelIndex& index : indices)
{
if (isExpanded(index))
{
list << QString::fromStdString(toString(index));
}
}
return list;
}
void TreeView::restoreExpandedState(const QStringList& list)
{
setUpdatesEnabled(false);
for (const QString& string : list)
{
QModelIndex index = fromString(string.toStdString(), *model());
setExpanded(index, true);
}
setUpdatesEnabled(true);
};
For a QFileSystemModel, you can't use persistentIndexList().
Here is my work around. It works pretty well, even if I do say so myself. I haven't tested to see what happens if you have a slow loading filesystem, or if you remove the file or path.
// scrolling code connection in constructor
model = new QFileSystemModel();
QObject::connect(ui->treeView, &QTreeView::expanded, [=](const QModelIndex &index)
{
ui->treeView->scrollTo(index, QAbstractItemView::PositionAtTop);//PositionAtCenter);
});
// save state, probably in your closeEvent()
QSettings s;
s.setValue("header_state",ui->treeView->header()->saveState());
s.setValue("header_geometry",ui->treeView->header()->saveGeometry());
if(ui->treeView->currentIndex().isValid())
{
QFileInfo info = model->fileInfo(ui->treeView->currentIndex());
QString filename = info.absoluteFilePath();
s.setValue("last_directory",filename);
}
// restore state, probably in your showEvent()
QSettings s;
ui->treeView->header()->restoreState(s.value("header_state").toByteArray());
ui->treeView->header()->restoreGeometry(s.value("header_geometry").toByteArray());
QTimer::singleShot(1000, [=]() {
QSettings s;
QString filename = s.value("last_directory").toString();
QModelIndex index = model->index(filename);
if(index.isValid())
{
ui->treeView->expand(index);
ui->treeView->setCurrentIndex(index);
ui->treeView->scrollTo(index, QAbstractItemView::PositionAtCenter);
qDebug() << "Expanded" << filename;
}
else
qDebug() << "Invalid index" << filename;
} );
Hope that helps someone.
My approach was to save the list of expanded items (as pointers) and when restoring, only set as expanded only the items in this list.
In order to use the code below, you may need to replace TreeItem * to a constant pointer to your object (that doesn't change after a refresh).
.h
protected slots:
void restoreTreeViewState();
void saveTreeViewState();
protected:
QList<TargetObject*> expandedTreeViewItems;
.cpp
connect(view->model(), SIGNAL(modelAboutToBeReset()), this, SLOT(saveTreeViewState()));
connect(view->model(), SIGNAL(modelReset()), this, SLOT(restoreTreeViewState()));
...
void iterateTreeView(const QModelIndex & index, const QAbstractItemModel * model,
const std::function<void(const QModelIndex&, int)> & fun,
int depth=0)
{
if (index.isValid())
fun(index, depth);
if (!model->hasChildren(index) || (index.flags() & Qt::ItemNeverHasChildren)) return;
auto rows = model->rowCount(index);
auto cols = model->columnCount(index);
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
iterateTreeView(model->index(i, j, index), model, fun, depth+1);
}
void MainWindow::saveTreeViewState()
{
expandedTreeViewItems.clear();
iterateTreeView(view->rootIndex(), view->model(), [&](const QModelIndex& index, int depth){
if (!view->isExpanded(index))
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(item && item->getTarget())
expandedTreeViewItems.append(item->getTarget());
}
});
}
void MainWindow::restoreTreeViewState()
{
iterateTreeView(view->rootIndex(), view->model(), [&](const QModelIndex& index, int depth){
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(item && item->getTarget())
view->setExpanded(index, expandedTreeViewItems.contains(item->getTarget()));
});
}
I think this implementation gives extra flexibility compared to some of the others here. At least, I could not make it work with my custom model.
If you want to keep new items expanded, change the code to save the collapsed items instead.

QComboBox inside QTreeWidgetItem

Is there something similar to the (PyQT)
QTreeWidgetItem.setCheckState(0, Qt.Checked) but for the combo box?
I can't see anything in the reference, so how can I insert a custom QComboBox as one of the elements within QTreeWidgetItem?
Use QTreeWidget::setItemWidget ( QTreeWidgetItem * item, int column, QWidget * widget ) to put the combo box into the cells.
For example, let's make all rows of the second column of a 2-column QTreeWidget to all be combo boxes:
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it) {
QComboBox *comboBox = new QComboBox(this);
comboBox->addItems(QStringList() << "item1" << "item2");
ui->treeWidget->setItemWidget(*it, 1, comboBox);
++it;
}
Our example widget now looks like this:
I know this is an old question but I think I have a more thorough answer. To get any functionality out of the QComboBox, you'll probably need to subclass it. Here's the solution that I came up with:
#ifndef COMBOBOXITEM_H
#define COMBOBOXITEM_H
#include
class ComboBoxItem : public QComboBox
{
Q_OBJECT
private:
QTreeWidgetItem *item;
int column;
public:
ComboBoxItem(QTreeWidgetItem*, int);
public slots:
void changeItem(int);
};
ComboBoxItem::ComboBoxItem(QTreeWidgetItem *item, int column)
{
this->item = item;
this->column = column;
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(changeItem(int)));
}
void ComboBoxItem::changeItem(int index)
{
if(index >=0)
{
item->setData(this->column, Qt::UserRole, this->itemText(index));
qDebug() item->data(this->column, Qt::UserRole).toString();
}
}
#include "moc_ComboBoxItem.cpp"
#endif // COMBOBOXITEM_H
////// Sample implementation..
lst = new QTreeWidget;
// Snip
QTreeWidgetItem *itm = new QTreeWidgetItem;
// Snip
ComboBoxItem *cmb = new ComboBoxItem(itm, 1);
cmb->addItem("One");
cmb->addItem("Two");
cmb->addItem("Three");
cmb->addItem("Four");
lst->setItemWidget(itm, 1, cmb);
I hope that helps someone in need of a QComboBox inside of a QTreeWidgetItem!
Use
setItemWidget(QTreeWidgetItem( ), column, QWidget( ) )
.Just add your QComboBox() as a parameter, as it inherits QWidget() so it is compatible.
tree = QTreeWidget()
cmb = QComboBox()
cmb.addItem("Item1", 'value1')
cmb.addItem("Item2", 'value2')
cmb.addItem("Item3", 'value3')
item = QTreeWidgetItem(tree.invisibleRootItem())
column = 0
item.setData(column, Qt.EditRole, 'NameYouWant')
column += 1
tree.setItemWidget(item, column , cmb)
Here is small fix to the another posters method. I found that is uses Data to update the box How ever I made small change to setText updater for the method.
#ifndef COMBOBOXITEM_H
#define COMBOBOXITEM_H
#include <QtGui>
class ComboBoxItem : public QComboBox
{
Q_OBJECT
private:
QTreeWidgetItem *item;
int column;
public:
ComboBoxItem(QTreeWidgetItem*, int);
public slots:
void changeItem(int);
};
ComboBoxItem::ComboBoxItem(QTreeWidgetItem *item, int column)
{
this->item = item;
this->column = column;
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(changeItem(int)));
}
void ComboBoxItem::changeItem(int index)
{
if(index >=0)
{
this->item->setText(this->column, this->currentText());
}
}
#include "moc_ComboBoxItem.cpp"
#endif // COMBOBOXITEM_H
////// Sample implementation..
lst = new QTreeWidget;
// Snip
QTreeWidgetItem *itm = new QTreeWidgetItem;
// Snip
ComboBoxItem *cmb = new ComboBoxItem(itm, 1);
cmb->addItem("One");
cmb->addItem("Two");
cmb->addItem("Three");
cmb->addItem("Four");
lst->setItemWidget(itm, 1, cmb);
This is easiest method:
QComboBox *cb = new QComboBox(this);
QStringList cbTexts;
cbTexts << tr("First") << tr("Second") << tr("Third");
cb->addItems(cbTexts);
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
ui->treeWidget->addTopLevelItem(item);
ui->treeWidget->setItemWidget(item, [colum here], cb);
for (int col = 0; col < [num colums]; ++col) ui->treeWidget->resizeColumnToContents(col);

Resources