QStandardItemModel within QTtableview - qt

I am using QStandardItemModel inside QTtableview. Is it possible to add checkbox in on column cell & combobox in another column cell.
So that i can select predefined option from the combo box.
Please suggest how to achieve this.

For the first:
You can setFlags() on an QStandardItem to make it checkable:
Qt::ItemFlags QStandardItem::flags() const
void QStandardItem::setFlags ( Qt::ItemFlags flags )
Qt::ItemIsUserCheckable
( enum Qt::ItemFlag )
For the second:
You should create your own Custom Delegate class inheriting QStyledItemDelegate and reimplement the createEditor, setEditorData and setModelData methods. Check this link and, for a more complete example, the SpinBox Delegate example
Edit: Once you got your custom delegate class, you have to tell your view to use it in a given column with QAbstractItemView::setItemDelegateForColumn

Related

How to share a model's item between a QML ListView and a Grid

I would like to know the best way to achieve the following thing in QML:
I have a ListView with droppable elements and a Grid initialy filled with DropArea. The ListView uses a model derived from QAbstractItemModel. I would like to drop an element on the grid and interact with it (rename it for instance). For now, any modifications in the ListView update the model, but how modifications of the element in the grid could update the model ?
There can be multiple items dropped in the grid corresponding to a subset of ListView's model. I do not know how can I achieve this. The Package can not be used because the Grid is not a GridView and Items must be moved/set at specific positions. So I tried to:
create a ListView displayed on the dropped item, using the same model as the source ListView used to drag items,
set the same rootIndex, then the same index
I am close to a solution but I think it is not the best way to do this.
Any clue ?
Thanks
I would like to have different visual representation of the same model
item in a ListView and in a component in a Grid. So, a modification of
the item in the ListView should update the item in the Grid and
vice-versa.
If your model's data is not QObject derived with NOTIFY and properties, notifications of changes can only be made through the model.
And since you won't be using the model for the grid, that means your model has to use underlying QObject instances. A generic model object might come in handy.
When you have that you only need to reference the underlying QObject model item and use bindings to set up your list view delegate. Then when you drag and drop, you only need to pass a reference to the QObject to that other visual representation which you will be creating inside the grid.
Lastly, take care not to be left with dangling references when you remove items from the model, as that will most likely hard-crash your application. Use the onDestruction attached signal to clear elements from the grid as the model item objects are destroyed.
I finally found a solution by create a C++ type which inherits from QObject an which can be embedded in a QML object. This type has read/write properties and is initialized with the same model as the ListView. The interesting methods are:
/* writing to a property **from QML** goes here */
void ModelItem::setName(const QString& name)
{
setModelData(GroupMemberModel::Nom, name);
}
/* then here */
bool ModelItem::setModelData(GroupMemberModel::Role role, const QVariant& value)
{
return m_model->setData(m_modelIndex, value, role);
}
/* any changes in the model fall here (signals/slots mecanism)*/
void ModelItem::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
{
if(m_modelIndex.row() < topLeft.row() || m_modelIndex.row() > bottomRight.row())
return;
if(m_modelIndex.column() < topLeft.column() || m_modelIndex.column() > bottomRight.column())
return;
//Index is modified, emit signal
foreach(int role, roles) {
emitDataChanged(role);
}
}
/* **notify QML** by emit signal on property */
void ModelItem::emitDataChanged(int role) const
{
if(role < (Qt::UserRole+1))
role+=Qt::UserRole+1;
switch(role)
{
case GroupMemberModel::Nom:
emit nameChanged();
break;
default:
qDebug() << "ModelItem::dataChanged, unknown role";
break;
}
}
This works as needed and is simplier than what I thought.

QlistView setCurrentIndex() not working

I am facing a problem with a QListView component.
I created a simple form with a listview and a tableview.
Then I put this code, both widgets populate with the data model as I want:
QSqlQueryModel * modela = new QSqlQueryModel();
QSqlQueryModel * modelb = new QSqlQueryModel();
[...]
ui->listView->setModel(modela);
ui->tableView->setModel(modelb);
[...]
void MyWindow::on_listView_clicked(const QModelIndex &index)
{
ui->tableView->setCurrentIndex(ui->listView->currentIndex());
}
void MyWindow::on_tableView_clicked(const QModelIndex &index)
{
ui->listView->setCurrentIndex(ui->tableView->currentIndex());
// FAILS, does not react...
}
The first slot (when I click any item in the listview widget) works as expected, it automatically selects the corresponding item in the tableview widget, but the second case does not work, it just does not select any item in the listview...
What I want is that whatever item the user clicks in the tableview gets selected in the listview.
Is it possible?
I tried hard, looking for examples and the official qt documentation, but I don't find the right way to do (also tried to connect with signal/slots, but I don't know how to exactly connect both widgets).
Thanks in advance.
QModelIndex is an integral part of a certain QAbstractItemModel. It means that you can't use an index from model A to select an item in a view of model B.
QModelIndex is not just a couple of x,y. It also keeps a pointer to a model which created it.
So if you need to select the same row as selected in the first view, you need to extract a row from the first index, then get a right index in the second model and use it to select an item in the second view:
void selectTheSameRow(const QModelIndex& indexFromModelA)
{
int row = indexFromModelA.row();
QModelIndex indexFromModelB = modelB->index(row, 0);
viewB->setCurrentIndex(indexFromModelB);
}

Interact with editor widgets' data in QTableView

I'm using an editable QTableView + QStandardItemModel.
While editing a cell in a table view, I'd like to do something according to the new input data in this specific cell when committing the new data into the table view.
To do this, I need the new input data and the current model index (or column & row number).
I tried some slots such as
virtual void closeEditor (QWidget * editor,
QAbstractItemDelegate::EndEditHint hint)
and
virtual void commitData ( QWidget * editor ).
commitData seems to be what I need, however, the parameter is only the editor and I cannot figure out how to obtain the text in this editor widget.
I looked QTextEdit but it's not a inherited class of QWidget.
I wonder if there's any way to obtain the data (text) and axis (column, row) of an editor widget?
I suggest to implement your own item delegate, inheriting QStandardItemDelegate (or QAbstractItemDelegate). There you can override
void setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
Simply do you your custom processing and then call QStandardItemDelegate::setModelData(...) to make sure that your model is updated with the newly edited data.
The itemChanged(QStandardItem*) signal is emitted by a QStandardItemModel whenever an item's data changes.
From the given QStandardItem, you can retrieve the row and column directly. To get the displayed text, pass Qt::DisplayRole to the item's data() method.

How to customize QCombobox with multiple comlumns

I am using QCombobox, i want to every item in QCombobox displays three icons. But currently, every item in QCombobox only displays one icon!
Every icon should be changed dynamically.
You should create new custom QAbstractItemDelegate and set it to QComboBox using void QComboBox::setItemDelegate ( QAbstractItemDelegate * delegate ) api.
In delegate, you need to implement
virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const = 0
as you required.
You will also need to use following API to provide different icon to combo box in userData, that you can use in deletegate's paint method to retrieve icon and draw it.
void QComboBox::addItem ( const QString & text, const QVariant & userData = QVariant() )
Summary:
When I implement as above, there icons only show as drop down list clicked. In normal situation, the text only show. So, for three icons and text show in normal situation we must reimplement paintEvent of QCombobox in case subclass QCombobox or using eventFilter to catch paintEvent of QCombobox without subclass QComboBox!
Thank for your all response!
Reimplement paintEvent, or use big icon image with all 3 icons on it.

How to insert QPushButton into TableView?

I am implementing QAbstractTableModel and I would like to insert a QPushButton in the last column of each row. When users click on this button, a new window is shown with more information about this row.
Do you have any idea how to insert the button? I know about delegating system but all examples are only about "how to edit color with the combo box"...
You can use
QPushButton* viewButton = new QPushButton("View");
tableView->setIndexWidget(model->index(counter,2), viewButton);
The model-view architecture isn't made to insert widgets into different cells, but you can draw the push button within the cell.
The differences are:
It will only be a drawing of a pushbutton
Without extra work (perhaps quite a bit of extra work) the button won't be highlighted on mouseover
In consequence of #1 above, you can't use signals and slots
That said, here's how to do it:
Subclass QAbstractItemDelegate (or QStyledItemDelegate) and implement the paint() method. To draw the pushbutton control (or any other control for that matter) you'll need to use a style or the QStylePainter::drawControl() method:
class PushButtonDelegate : public QAbstractItemDelegate
{
// TODO: handle public, private, etc.
QAbstractItemView *view;
public PushButtonDelegate(QAbstractItemView* view)
{
this->view = view;
}
void PushButtonDelegate::paint(
QPainter* painter,
const QStyleOptionViewItem & option,
const QModelIndex & index
) const
{
// assuming this delegate is only registered for the correct column/row
QStylePainter stylePainter(view);
// OR: stylePainter(painter->device)
stylePainter->drawControl(QStyle::CE_PushButton, option);
// OR: view->style()->drawControl(QStyle::CE_PushButton, option, painter, view);
// OR: QApplication::style()->drawControl(/* params as above */);
}
}
Since the delegate keeps you within the model-view realm, use the views signals about selection and edits to popup your information window.
You can use setCellWidget(row,column,QWidget*) to set a widget in a specific cell.

Resources