Subclassing QTreeWidgetItem - qt

I am trying to subclass QTreeWidgetItem so I can fill-in class with data I need.
Went through all the code and couldn't find one complete example on how to do it right.
In my implementation something is wrong because application won't start up, but if I do it with QTreeWidgetItem then it works without any problems.
So I have created subclass as H file:
#ifndef XITEM_H
#define XITEM_H
#include <QTreeWidget>
class XItem : public QObject,public QTreeWidgetItem
{
Q_OBJECT
public:
XItem ();
void setText(int column, const QString &atext);
void addChild(QTreeWidgetItem *child);
};
#endif
And C file:
#include "X.h"
XItem ::XItem (): QTreeWidgetItem(UserType)
{
}
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
And code that runs it all:
QTreeWidget * tree = ui->treeWidget;
QTreeWidgetItem * topLevel = new QTreeWidgetItem();
topLevel->setText(0, "This is top level");
for(int i=0; i<5; i++)
{
//QTreeWidgetItem * item = new QTreeWidgetItem();
XItem *item = new XItem();
item->setText(0,"item " + QString::number(i+1));
topLevel->addChild(item);
}
tree->addTopLevelItem(topLevel);
If I run it like this application hangs and if I comment:
XItem *item = new XItem();
and un-comment:
QTreeWidgetItem * item = new QTreeWidgetItem();
it works.
Could anyone help out on this please.
Kind regards!

Anyway... after all years still simple mistake as this one can come along long hours coding.
ERROR:
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
Should be:
void XItem::setText(int column, const QString &atext){
QTreeWidgetItem::setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
QTreeWidgetItem::addChild(child);
}

Related

QT Signals and Slots, dynamic menubar

I am new to qt and I want to know how to make a dynamic menu.
I did get it to make new submenus but I don't know how I can implement the "triggered() function" of these dynamic made submenus, so that I have access to what happens if I want to click on such a new submenu.
Here what I have so far (with: vector<QString> = vec; and some .ui Window named "New_Window")
in mainwindow.cpp
in some function:
QMenu *menu = this->menuBar()->addMenu("Chat Members");
for (int i = 0; i < vec.size(); ++i){
QString name = vec.at(i);
QAction *act = menu->addAction(name);
New_Window* new_window = new New_Window;
QObject::connect(act,SIGNAL(triggered()),
new_window,SLOT(actionReaction()));
}
here is an example of how a signal slot with a dynamic interface works ,
class A is created after starting the program, then the user clicks on a button from class A, for example, a class A is created many times and we need to determine from what object we get a signal to press the button, so
class A : public QMainWindow
{
Q_OBJECT
public:
A(QWidget *parent = nullptr);
~A();
void setID(const int id);
void getId() const;
signals:
void onButtonPress(int ID);
private:
int mID;
};
here we create a new class A and store it in the vector in such a way,
QVector<A*> mCreatingClassA;
void createNewClassA
{
QVector<A*> mCreatingClassA;
....
A* a = new A();
int id = // create your unique ID
a->setId(id);
connect(a,SIGNAL(onButtonPress(int)),this,SLOT(onyourSlot(int)));
mCreatingClassA.push_back(a);
....
}
detect the object from which the signal was received)
void onyourSlot(int ID)
{
for (int i = 0; i < mCreatingClassA.size(); ++i) {
if(mCreatingClassA[i]->getId()==ID)
{
mCreatingClassA[i] // received a signal from this object
}
}
}

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!

QStandardItemModel header with widget and text

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();

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