Editable checkbox only column in QTableView - c++

I have column with a checkbox in a QTableView. The checkbox is generated by:
returning Qt::ItemIsUserCheckable in overridden flags member function
in overridden data() function I return a Qt::CheckState for role == Qt::CheckStateRole according to data
Works, see screenshot.
But beside the checkbox I have some editable textbox in the column. How can I get rid of this textbox (where I have entered "dsdsdsds" for demonstration? Clarification, the checkbox shall be editable, but nothing else.
As requested, I can only show simplified version
Qt::ItemFlags MyClass::flags(const QModelIndex &index) const {
Qt::ItemFlags f = QAbstractListModel::flags(index);
... return f if index is not target column ....
// for target column with checkbox
return (f | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; )
}
QVariant MyClass::data(const QModelIndex &index, int role) const {
.. do something for other columns
.. for checkbox column
if (role != Qt::CheckStateRole) { return QVariant(); }
bool b = ... get value for checkbox column
Qt::CheckState cs = b ? Qt::Checked : Qt::Unchecked;
return QVariant(static_cast<int>(cs));
}
If I remove Qt::ItemIsEditable then the checkbox is read only too. I later found an SO answer with a similar approach.
Remark: No duplicate of A checkbox only column in QTableView

Replace the flag
Qt::ItemIsEditable
with the flag
Qt::ItemIsEnabled
The first one tells Qt to create an editor for the value present in the model, which seems to be a texteditor in your case.
If the value is of type bool then a dropdown list containing true and false would be shown instead.

Related

Qt checkboxes in QTableView

I'm using this code to query sqlite and put the results in a QTableView.
//MainWindow.cpp
void MainWindow::on_pushButton_clicked()
{
QSqlQueryModel * modal=new QSqlQueryModel();
connOpen();
QSqlQuery* qry=new QSqlQuery(mydb);
qry->prepare("select * from database");
qry->exec();
modal->setQuery(*qry);
//from stack
modal->insertColumn(0);
ui->tableView->setModel(modal);
//from stack
ui->tableView->resizeColumnsToContents();
int p;
for(p=0; p<modal->rowCount(); p++)
{
ui->tableView->setIndexWidget(modal->index(p,0),new QCheckBox());
}
connClose();
qDebug() <<(modal->rowCount());
}
I've seen several examples of the web for adding checkboxes to a column, but I'm not quite sure what to use for my simple example.
This answer suggests a few lines that doesn't seem standard.
There are more examples like this and this one that appear to outline what I need, but it's unclear to where you place the code.
What I intend to do is to have column 1 checkable. On next btn press, If checked those rows of data get written to a file.
I still need to understand how to loop thru the selected data, or perhaps I need to get the ids of the checked rows and do another query.
Questions:
How do you add 1 column of editable checkboxes to QTableView?
How do you loop through values in the QTableView data, so values of the checked rows can be accessed?
How do you check all/none?
I think the best way to have a column of checkable cells is to create your item model, e.g. by subclassing the QSqlQueryModel.
You must reimplement the flags() method to make checkable the cells.
Also you need to reimplement the data() method to return the check state and the setData() method and to set the check state. You must implement your own logic to keep track of the check state of every rows (e.g. using an array of Qt::CheckState that you must initialize and resize when the model data changes).
Yuo can start with something like this:
class MyModel : public QSqlQueryModel
{
public:
Qt::ItemFlags flags(const QModelIndex & index) const
{
if(index.column() == 0)
return QSqlQueryModel::flags(index) | Qt::ItemIsUserCheckable;
return QSqlQueryModel::flags(index);
}
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const
{
if(index.column() == 0 && role == Qt::CheckStateRole)
{
//implement your logic to return the check state
//....
}
else
return QSqlQueryModel::data(index, role);
}
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)
{
if(index.column() == 0 && role == Qt::CheckStateRole)
{
//implement your logic to set the check state
//....
}
else
QSqlQueryModel::setData(index, value, role);
}
};
Se also:
Model Subclassing
QAbstractItemModel documentation

QTreeView: hierarchical context for multi-level columns

I'm looking for a better way to display multi-level hierarchical data in a tree where the meaning of each column changes depending on the level in the tree.
I am using QTreeView and QAbstractItemModel to display my model data.
Each model row has a different number of columns and different column names depending on its level in the hierarchy.
In order to give context to the data displayed in the tree, I need to have column headers for each level in the hierarchy.
The problem is that QTreeView only has 1 set of column headers.
Current method
At the moment I'm changing the headers each time the selected row changes.
I do this by connecting to the tree view's selectionModel, and emitting a signal with the new QModelIndex each time the selection changes
void Window::slotOnSelectionChanged(const QItemSelection& new_selection, const QItemSelection& old_selection)
{
QItemSelectionModel* selection_model = _view->selectionModel();
QModelIndex index = selection_model->currentIndex();
if (index.isValid())
emit selectedIndexChanged(index);
}
In my model I connect to this signal, and when its fires, store the selected row, and force a column header update
void Model::slotOnSelectedIndexChanged(QModelIndex index)
{
assert(index.isValid());
_selected_row = modelRow(index);
emit headerDataChanged(Qt::Horizontal, 0, _root->numColumns());
}
In the QAbstrateItemModel::headerData callback I then use selected_row to get the header for the currently selected row
QVariant Model::headerData(int i, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
switch (orientation)
{
case Qt::Horizontal:
return QVariant(_selected_row->header(i));
...
Result
The result can be seen below - notice how the column headers change as the selected row changes.
Problem
It's not immediately obvious by just looking at the view what each datum is, and therefore the user is required to change rows in order to see what each column actually means.
What I'd like is to have some sort of embedded column header row, 1 per level in the hierarchy.
Something like this:
Questions
Is this possible?
If there is a better way to give context to the data in the tree, please do offer a suggestion.
At the suggestion of #Kuba Ober I added an extra row at position 0 in each hierarchy of the tree. It has no children.
The model is then configured to special case for index.row() == 0, knowing that this row is a header row rather than a data row.
eg: in Model::flags the header row is not editable
Qt::ItemFlags Model::flags(const QModelIndex& index) const
{
Qt::ItemFlags item_flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
// header row is not editable
if (index.row() != 0)
item_flags |= Qt::ItemIsEditable;
return item_flags;
}
I now return empty strings for headerData as the headers are in the tree itself
QVariant Model::headerData(int i, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
switch (orientation)
{
case Qt::Horizontal:
return QVariant(); // no column header, it's already in the tree
...
I also change the background color for the header so it stands out
QVariant Model::data(const QModelIndex& index, int role) const
{
switch (role)
{
case Qt::BackgroundColorRole:
if (index.row() == 0) // header row
return QColor(Qt::darkGray);
break;
...
The result is almost exactly what I was looking for

QTableView - how to prevent selection change

I have QTableView with custom table model. User can select row in the table and in specific situations I want to prevent change of the current selection.
Reselection of previously selected row is not an ideal solution (signals about the change are emited).
So what is the easies solution?
Is there some option I do not see?
Do I need to subclass QTableView?
You can make a View not selectable with QAbstractItemView::setSelectionMode(QAbstractItemView::NoSelection)
And you can do it in a per item basis too, using Qt::ItemIsSelectable
Qt::ItemFlags QAbstractItemModel::flags(const QModelIndex & index) const [virtual]
Edit (comments):
You have a custom model, so you can set a current row member variable. then, override flags:
Qt::ItemFlags YourModel::flags(const QModelIndex & index) const
{
if( _current_row > 0 && index.row() != _current_row)
{
return QAbstractItemModel::flags() | ^Qt::ItemIsSelectable;
}
else
{
return QAbstractItemModel::flags() | Qt::ItemIsSelectable;
}
}
Of course, dindt tried, but you get the idea.
Updated so, if you set current_row to -1, all are selectable
You can reselect the previously selected row. In the signal handler, call blocksignals(true) before reselection and then call blocksignals(false) to allow signalling again.

Qt - Disable editing of cell

I have a QTableView (model based) and I want to disable the editing capability of a particular cell, let's say row 0, column 1.
How can I do this? Please note that I still want other cells in this row enabled for editing.
If you are using a custom table model, you can implement the Qt::ItemFlags QAbstractItemModel::flags ( const QModelIndex & index ) const method and return a set of flags where the Qt::ItemIsEditable flag is not set for the cells you do not want to edit. Suppose MyTableModel is inherited from QAbstractTableModel:
Qt::ItemFlags MyTableModel::flags ( const QModelIndex & index ) const {
Qt::ItemFlags flags = Qt::NoItemFlags;
if (index.row() == 0 && index.column() == 1) {
return flags;
}
return flags | Qt::ItemIsEditable;
}

My delegate doesn't display checkboxes

Guys I've implemented my delegate class based on QStyledItemDelegate and the problem I have is that it doesn't display checkboxes next to the text which is displayed in the listView.
Before I've used my delegate I've those checkboxes displayed in my listView so I know that's the problem lays in this delegate class.
Any thoughts?
EDIT
void Display_Delegate::paint( QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex &index) const
{
QString model_data = index.data().toString();
QFontMetrics metrics = view_->fontMetrics();
int view_width = view_->width();
auto modified_str = adjust_text(metrics,model_data,view_width);//this just makes the string to fit into view, don't bother about it.
QStyleOptionViewItemV4 style_option = option;
initStyleOption(&style_option,index);
QPalette::ColorGroup color_group = style_option.state & QStyle::State_Selected ? QPalette::Active : QPalette::Inactive;
if (style_option.state & QStyle::State_Selected)
{
// painter->setPen(style_option.palette.color(color_group, QPalette::Highlight));
painter->setBackgroundMode(Qt::OpaqueMode);
QColor color(148,231,245,100);
painter->setBackground(QBrush(color));
}
else
{
painter->setPen(style_option.palette.color(color_group, QPalette::Text));
}
painter->drawText(option.rect,modified_str);
}
Qt::CheckState QStyleOptionViewItemV4::checkState
If this view item is checkable, i.e., ViewItemFeature::HasCheckIndicator is true, checkState is true if the item is checked; otherwise, it is false.
I found in the methods this fairly obscure reference to having a check indicator. It says that if you want to make the item "checkable" then set this style option. So try something like:
style_option.ViewItemFeatures = QStyleOptionViewItemV2::HasCheckIndicator;