Paradigmatic way to layout QTableView vertically - c++

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

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
{
...
}
...
}
...
}

Show image in a column of QTableView from QSqlTableModel

I'm curious about how I can display an image from my database in a QTableView.
Is there something like QTableWidgetItem that I am able to use it in QTableView?
I use QSqlTableModel.
A rough idea is to use QStandardItem::setData to set a QPixmap(transformed into QVariant) on it, then you can set the QStandardItem on the QStandardItemModel.
Sequence: QImage--->QPixmap--->QVariant--->QStandardItem--->QStandardItemModel
For example:
QStandardItemModel *model = new QStandardItemModel;
QImage image(":/cat/lovers/own/myCat.jpg");
QStandardItem *item = new QStandardItem();
item->setData(QVariant(QPixmap::fromImage(image)), Qt::DecorationRole);
model->setItem(0, 0, item);
ui->tableView->setModel(model);
You will have to resize images or cell size, depends on what you need.
[Edit]
If you are using QSqlTableModel, just keep using it. All we need to do is make those path strings into QPixmap and set the item role to be Qt::DecorationRole in that column.
As the document says:
Each item has a number of data elements associated with it and they can be retrieved by specifying a role (see Qt::ItemDataRole) to the
model's data() function.
To do this, the concept is simple: provide QTableView with QVariant of QPixmap as QTableView render them according to Qt::DecorationRole.
You may subclass QSqlTableModel and reimplement the virtual function QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) and make the image column return the QPixmap as QVariant, with the decoration role. So do something like this:
QVariant CustomSqlTableModel::data(const QModelIndex &idx, int role = Qt::DisplayRole) const
{
if (idx.column() == imageColumn) {
QString imgFile = QSqlTableModel::data(idx, Qt::DisplayRole); // get path string
if (role == Qt::DisplayRole)
return QString(); // return the path string for display role
QImage image(imgFile);
/* some modification to the image, maybe */
QPixmap pixmap(imgFile);
if (role == Qt::DecorationRole)
return pixmap; // return QPixmap for decoration role
if (role == Qt::SizeHintRole)
return pixmap.size(); // in case need the image size
}
return QSqlTableModel::data( idx, role ); // use original data() outside the imageColumn
}
Besides, you can also try subclassing QStyledItemDelegate and reimplement paint() function to customize your own delegate, but that will require a more complicated work. An example using delegate can be found here. You can paint whatever you want with delegate, even a button.
*Sorry the code is not tested, as I don't have a database on hand.
I got a question to read image from tableview, so I find the solution as fallow:
QImage name_image = table_store_multi_model_->item(i_row,0)->data(Qt::DecorationRole).value<QPixmap>().toImage();
Generally, we read data with data(), but here need a parameter "Qt::DecorationRole";

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

Displaying multiple icons in a single cell of a QTableView

I am writing a small gui app with QT4.5 in QtCreator.
The main screen on the app contains a QTreeView with two columns, the first is text the second is a group of icons. These icons represent the last few states of the item displayed in the row.
I am not sure what the best way to do this is. I have currently implemented this by generating a QPixmap the model's data() method.
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch(index.column()) {
case 0:
return item_.at(index.row()).title();
}
}
if (role == Qt::DecorationRole) {
switch(index.column()) {
case 1:
return makeImage(item_.add(index.row()).lastStates());
}
}
return QVariant();
}
QVariant MyModel::makeImage(const QList<MyState> &states) const
{
const int IconSize = 22;
QPixmap image(IconSize * states.size(), IconSize);
QPainter painter(&image);
painter.fillRect(0, 0, IconSize * count, IconSize, Qt::transparent);
for (int i = 0; i < states.size(); ++i) {
QIcon * icon = stateIcon(state.at(i));
icon->paint(&painter, IconSize * i, 0, IconSize, IconSize);
}
return image;
}
This works but for some small problems, the background which should be transparent is full of random noise, even filling this with a transparent colour does not fix it.
Second this does not seem very efficient, I am generating a new Image every time this is called, should I not just draw the icons onto the widget for the cell?
What is the best way to display multiple icons in a single cell?
I would create a custom delegate, based on a hbox, into which you can place all the pictures. Have a look at delegates in the Qt Documentation about model view programming.

Selecting an index in a QListView

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