Selecting an index in a QListView - c++

This might be a stupid question, but I can't for the life of me figure out how to select the row of a given index in a QListView.
QAbstractItemView , QListView's parent has a setCurrentIndex(const QModelIndex &index). The problem is, I can't construct a QModelIndex with the row number I want since the row and column field of the QModelIndex has no mutators.
QTableView, which also inherits from QAbstractItemView has a selectRow(int row) function, why in the seven hells doesn't the QListView have this?
Good ol' windows forms has the SelectedIndex property on it's listviews.

This should help you get started
QModelIndex index = model->createIndex( row, column );
if ( index.isValid() )
model->selectionModel()->select( index, QItemSelectionModel::Select );

You construct the QModelIndex by using the createIndex(int row, int column) function of the model you gave to the view. QModelIndexes should only be used once, and must be created by the factory in the model.

My working sample at Qt4.8.0 (MSVC2010 Compiller) based on Michael Bishop
QStandardItemModel *Model = (QStandardItemModel *)this->ui->listView_OptionsCategories->model();
QModelIndex index = Model->index(this->ui->stackedWidget->currentIndex(), 0);
if ( index.isValid() )
this->ui->listView_OptionsCategories->selectionModel()->select( index, QItemSelectionModel::Select );

For Qt 6.3.x:
void selectRowInQListView(int row, QListView *listView) {
QModelIndex index = listView->model()->index(row, 0);
if (index.isValid()) {
//listView->selectionModel()->select(index, QItemSelectionModel::Select);
//listView->selectionModel()->select(index, QItemSelectionModel::Current);
listView->setCurrentIndex(index);
}
}

Related

Change what column of QTreeView displays expand/collapse icon

Qt version 4.8.4
I have a QTreeView that reflects a QAbstractItemModel-derived model. The model provides data in multiple columns. The expand/collapse icon for a row of the view is always displayed in the cell that is in the column that has a logical index of zero, even if that column has been moved so that it has a visual index other than zero.
Is QTreeView compelled to always draw the expand/collapse icon in logical column zero? Can that be customized, either via the QTreeView or the model?
The expand/collapse icon, along with all the other tree branching lines, is indeed compelled to always draw in logical column zero. In Qt/4.8.4/src/gui/itemviews/qtreeview.cpp, in the QTreeView::drawRow() method, it is hardcoded to only call QTreeView::drawBranches() if the headerSection (which contains the logical index of the column) is zero.
void QTreeView::drawRow( QPainter * painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
...
for ( int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection )
{
int headerSection = logicalIndices.at(currentLogicalSection);
...
// If the zeroeth logical column, ...
if ( headerSection == 0 )
{
...
// ... draw tree branches and stuff.
drawBranches(painter, branches, index);
}
else
{
...
}
...
}
...
}

Drag & Drop of hidden QStandardItemModel columns in QTreeView

Within a QTreeView, I would like to copy rows around by drag and drop. The corresponding Drag & drop settings look like:
this->setDragDropMode( QAbstractItemView::DragDrop );
this->setDropIndicatorShown( true );
This works fine unsing for the columns of the underlying QStandardItemModel which are visualised by the QTreeView. But not all columns of the model are visualised (see Hide future columns of QStandardItemModel in QTreeView):
void MyViewClass::columnCountChanged(int p_nOldCount , int p_nNewCount )
{
QTreeView::columnCountChanged( p_nOldCount, p_nNewCount );
for ( int i = MyViewClass::m_nColumnType; i < p_nNewCount; ++i )
{
setColumnHidden( i, true );
}
}
How can I copy the whole row of a QStandardItemModel by drag and drop in the QTreeView when not all columns are visualised by the QTreeView?
Found the solution:
One has to inherit / implement the QAbstractModel functions:
virtual QMimeData * mimeData(const QModelIndexList &indexes) const;
virtual bool dropMimeData(const QMimeData *p_grData, Qt::DropAction p_grAction, int p_nRow, int p_nColumn, const QModelIndex &p_grParentIdx);
virtual QStringList mimeTypes() const;
while mimeData needs to encode the data and dropMimeData needs to decode the data and needs to insert a new row / column with the draged data.

Paradigmatic way to layout QTableView vertically

I have a QTableView that shows a model with many columns.
The model contains something like vector<my_item_with_lots_of_fields>; for most applications the size of vector is below 5.
For aesthetic reasons, the model would look better flipped so that each entry would run from top to bottom.
One dirty solution is to change the model so that the row and column indexing is switched. Unfortunately this would break other widgets that access the model.
Is there some easy, paradigmatic way to achieve this effect without changing the underlying model? Perhaps to change the widget?
I think one approach would be to create a proxy model for this. You would then need to do two changes:
1) data method
QVariant MyProxyModel::data(const QModelIndex & index,
int role = Qt::DisplayRole) const
{
return myTableModel::data(QModelIndex(index.column(), index.row()), role);
}
2) headerData method
QVariant MyProxyModel::headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const
{
return myTableModel::headerData(section,
orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, role);
}

Change Row Label Start Index (vertical header) in QtableView

While displaying data, on a QTableView , due to an internal reason, i cannot display the first row and hence have to hide it, using
(qtableobj)->hideRow(0);
The problem is that, now the row labels start from 2.
How is it possible to start the index from 1, while keeping the first row hidden ?
Thanks.
You can try to involve the QSortFilterProxyModel that will filter out the first row in your model.
The code could look like:
class FilterModel : QSortFilterProxyModel
{
[..]
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
if (index.row() == 0) // The first row to filter.
return false;
else
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
}
Finally you need to set this model to your table view:
QTableView *table = new QTableView;
MyItemModel *sourceModel = new MyItemModel;
QSortFilterProxyModel *proxyModel = new FilterModel;
proxyModel->setSourceModel(sourceModel);
table->setModel(proxyModel);
UPDATE
Since the issue is in how the header view displays row numbers, here is the alternative solution which based on special handling of header data in the model:
class Model : public QAbstractTableModel
{
public:
[..]
virtual QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const
{
if (role == Qt::DisplayRole) {
if (orientation == Qt::Vertical) {
// Decrease the row number value for vertical header view.
return section - 1;
}
}
return QAbstractTableModel::headerData(section, orientation, role);
}
[..]
};
Set up the table view with the hidden first row.
QTableView *table = new QTableView;
Model *sourceModel = new Model;
table->setModel(sourceModel);
table->hideRow(0);
table->show();

How to put an image and a QProgressBar inside a QTableView?

I'm developing some kind of download manager and display the file name, it's size and the remaining bytes in a QTableView. Now I want to visualize the progress with a QProgressBar and display an image (to indicate whether it's an down- or upload). How can I add or display a QProgressBar and an image inside the QTableView?
If you are using QTableView, I presume you use a model linked to this view.
One solution would be to use delegates (see QItemDelegate) to paint the progress, In QItemDelegate::paint method you have to define, use QStyle of the widget (widget->style()) to paint the progress (use QStyle::drawControl with QStyle::CE_ProgressBarContents as control identifier).
Check the documentation from the example Star Delegate, to see how to define the delegate for the column you need.
Later edit: Example of defining the delegate paint method (code sketch, not really tested, take it as a principle, not fully working).
void MyDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionProgressBar progressStyle;
progressStyle.rect = option.rect; // Maybe some other initialization from option would be needed
// For the sake of the example, I assume that the index indicates the progress, and the next two siblings indicate the min and max of the progress.
QModelIndex minIndex = index.sibling( index.row(), index.column() + 1);
QModelIndex maxIndex = index.sibling( index.row(), index.column() + 2);
progressStyle.minimum = qvariant_cast< int>( minIndex.data( Qt::UserRole));
progressStyle.maximum = qvariant_cast< int>( maxIndex.data( Qt::UserRole));
progressStyle.progress = qvariant_cast< int>( index.data( Qt::UserRole));
progressStyle.textVisible = false;
qApp->style()->drawControl( QStyle::CE_ProgressBarContents, progressStyleOption, painter);
}
TrackDelegate::TrackDelegate(QObject *parent)
: QItemDelegate(parent)
--------------------------------------------------------------------------------
void TrackDelegate::paint( QPainter* painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionViewItem viewOption(option);
QImage image(m_RowBackGroundImagePath);
QPixmap pixmap(m_RowBackGroundImagePath);
qDebug()<<"forward"<<pixmap.width()<<pixmap.height();
pixmap.scaled(option.rect.width(),option.rect.height());
qDebug()<<"back"<<pixmap.width()<<pixmap.height();
qDebug()<<option.rect.width()<<option.rect.height();
QBrush brush(pixmap);
painter->save();
painter->fillRect(option.rect, brush/*QColor(238, 233, 233, 255)*/);
painter->restore();
viewOption.rect = QRect(option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height());
// viewOption.palette.setColor(QPalette::Text, QColor(Qt::red));
// viewOption.palette.setBrush ( QPalette::ButtonText, brush1);
QItemDelegate::paint(painter, viewOption,index);
int progress = index.model()->data(index,Qt::DisplayRole).toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = QRect(option.rect.x(), option.rect.y()+(SETHEIGHT - PROGRESSBARHEIGHT)/2, option.rect.width(), /*option.rect.height()*/PROGRESSBARHEIGHT);
//qDebug()<<progressBarOption.rect.x()<<progressBarOption.rect.y()<<progressBarOption.rect.height()<<progressBarOption.rect.width();
//qDebug()<<option.rect.x()<<option.rect.y()<<option.rect.height()<<option.rect.width();
progressBarOption.state |= QStyle::State_Enabled;
progressBarOption.direction = QApplication::layoutDirection();
progressBarOption.fontMetrics = QApplication::fontMetrics();
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.textAlignment = Qt::AlignCenter;
progressBarOption.textVisible = true;
progressBarOption.progress = progress < 0 ? 0 : progress;
progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress);
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
break;
}
You probably want to use QTableWidget for this. It has a method which allows you to add widgets like a QProgressBar. It's the "setCellWidget" method.
There is a slot in QProgressBar called setValue(int),
You can update it sending a signal to this progress bar from Your file manager.
This one should be designed in a way it could check or monitor download state and sends those signals periodically.
Good approach to manage up/down/finish images would be to have additional column in table with the image item.
It would be quite easy to update your image if the socket/connection/file corrupt state would change.
Writing any example would be actually writing you a program, so i suggest to post parts of the problems (if any) with code performed by yourself.
QTableView is not for displaying widgets in a layout. Use QGridLayout or some other suitable layout and put the widgets in that layout.