I need to put a custom widget inside a QTableView cell from a subclassed QAbstractTableModel.
I searched for already given solutions but no one catch my needs.
The custom widget must stay here all the time and not only in editing mode like with the QItemDelegate::createEditor.
The custom widget may be everything, i'm searching for a general solutions for all the widget not only QPushButtons or QCheckBox.
Sorry for my english.
You can use QAbstractItemView::setIndexWidget and overriding QAbstractItemView::dataChanged to achieve what you want as follows:
class MyTableView : public QTableView
{
protected:
void dataChanged(const QModelIndex &topLeft, const QModelIndex & bottomRight, const QVector<int> & roles)
{
QPushButton *pPushButton = qobject_cast<QPushButton*>(indexWidget(topLeft));
if (pPushButton)
pPushButton->setText(model()->data(topLeft, Qt::DisplayRole).toString());
else
QTableView::dataChanged(topLeft, bottomRight, roles);
}
};
void main ()
{
MyTableView table;
table.setIndexWidget(table.model()->index(0, 0, QModelIndex()),new QPushButton());
}
Note that it is an incomplete implementation, but it should show you how you can solve your problem. A real implementation should update all QPushButton between topLeft and bottomRight.
Related
I have a subclass of QStyledItemDelegate which at the moment does not reimplement any functions (for simplicity of the question).
With default QStyledItemDelegate implementation, when the user begins to edit text in a QTableView, the delegate draws a QLineEdit with the text from the model, and selects all of it (highlights all for editing).
The text represents file names such as "document.pdf". The user is allowed to edit this entire text, however, I only want to initially highlight the base name portion ("document") and not the suffix ("pdf"). How can I do this? (I don't need the logic of how to do this, I need to know how to get the QStyledItemDelegate to highlight a portion of text)
I've tried:
in setEditorData() used QLineEdit::setSelection() to highlight some text. This has no effect.
in paint() attempted to paint based on what other respondents have recommended to similar questions, but not success. I have little experience with QPainter. Here is an example: Adjusting the selection behaviour of QStandardItem with QStyledItemDelegate
Please help, and thanks in advance. A code snippet with say selecting the first 3 characters of text would be greatly appreciated.
As noted in my comments to the question, the problem with subclassing QStyledItemDelegate and trying to set any default selection in setEditorData like this:
void setEditorData(QWidget* editor, const QModelIndex &index)const{
QStyledItemDelegate::setEditorData(editor, index);
if(index.column() == 0){ //the column with file names in it
//try to cast the default editor to QLineEdit
QLineEdit* le= qobject_cast<QLineEdit*>(editor);
if(le){
//set default selection in the line edit
int lastDotIndex= le->text().lastIndexOf(".");
le->setSelection(0,lastDotIndex);
}
}
}
is that (in Qt code) after the view calls our setEditorData here, it tries to call selectAll() here when the editor widget is a QLineEdit. That means that whatever selection we provide in setEditorData will be changed afterwards.
The only solution I could come up with, was to provide our selection in a queued manner. So that, our selection is set when execution is back into the event loop. Here is working example:
#include <QApplication>
#include <QtWidgets>
class FileNameDelegate : public QStyledItemDelegate{
public:
explicit FileNameDelegate(QObject* parent= nullptr)
:QStyledItemDelegate(parent){}
~FileNameDelegate(){}
void setEditorData(QWidget* editor, const QModelIndex &index)const{
QStyledItemDelegate::setEditorData(editor, index);
//the column with file names in it
if(index.column() == 0){
//try to cast the default editor to QLineEdit
QLineEdit* le= qobject_cast<QLineEdit*>(editor);
if(le){
QObject src;
//the lambda function is executed using a queued connection
connect(&src, &QObject::destroyed, le, [le](){
//set default selection in the line edit
int lastDotIndex= le->text().lastIndexOf(".");
le->setSelection(0,lastDotIndex);
}, Qt::QueuedConnection);
}
}
}
};
//Demo program
int main(int argc, char** argv){
QApplication a(argc, argv);
QStandardItemModel model;
QList<QStandardItem*> row;
QStandardItem item("document.pdf");
row.append(&item);
model.appendRow(row);
FileNameDelegate delegate;
QTableView tableView;
tableView.setModel(&model);
tableView.setItemDelegate(&delegate);
tableView.show();
return a.exec();
}
This may sound like a hack, but I decided to write this until someone has a better approach to the problem.
Does anybody know how to implement/realize a QTreeView with different colors for subgroups of the QTreeView items?
Something like:
Does anybody has done something like that and could give me a link to an tutorial or how to, or sample code would also be good. Currently I have absolutely no idea how to build this.
I'm working with Qt 5.1.1 and using the QTreeView with the QFileSystemModel and the QItemSelectionModel.
I also thought of :
m_TreeView->setStyleSheet(...)
but this only sets the style for the whole treeView or only for the selected ones.
Any suggestions? Thanks a lot for your help!
There's Qt::BackgroundRole which can be used to return a QColor to be used by the view to paint an index' background.
As you use an existing item model class (QFileSystemModel), it would be easiest to put a proxy model on top of the file system model just doing the colorization.
Using QIdentityProxyModel:
class ColorizeProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit ColorizeProxyModel(QObject *parent = 0) : QIdentityProxyModel(parent) {}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if (role != Qt::BackgroundRole)
return QIdentityProxyModel::data(index, role);
... find out color for index
return color;
}
};
To use it:
QFileSystemModel *fsModel = new QFileSystemModel(this);
ColorizeProxyModel *colorProxy = new ColorizeProxyModel(this);
colorProxy->setSourceModel(fsModel);
treeView->setModel(colorProxy);
If you need anything more fancy, (like special shapes etc.), you'd need your own item delegate with custom painting (see QStyledItemDelegate).
I want to check the text of a row in QListView before the user is editing it. If it doesn't fit a pattern, I don't want to accept it.
Currently I have a QListView and QStandardItemModel. I can easily add and remove items via the QStandardItemModel. I also set the model of the list view.
Are there some delegates or event function(s) on the list or the model for editing?
you can overload data() and setData() functions from QStandardItemModel, then when user tries to edit item your setData will be called with Qt::EditRole and there you can do your processing.
http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#setData
If I understand you correctly, you want to check the value of an item at the time the user attempts to enter the edit mode?
Using a delegate should work for this fairly well:
class MyItemDelegate : public QItemDelegate {
public:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
if(index.data() == /* do whatever check you want here */) {
return NULL; // Prevent editing
}
return QItemDelegate::createEditor(parent, option, index);
}
};
listView->setItemDelegate(new MyItemDelegate());
It seems there is zero built-in selection support with my choice of QListView -> QAbstractListModel. Do I have to write everything from scratch? the catching of a selection event in the UI, the marking of the model item as selected, etc? It seems there is no out-of-the-box support for this.
the weird thing is that there is a QItemSelectionModel that does support this, but you cannot use it with QListView as it’s not derived from QAbstract….
Should my model class use multiple inheritance to inherit both from QItemSelectionModel and QAbstractListModel? Otherwise I don’t see how I can avoid having to re-writing this functionality myself.
My final goal is for the delegate that draws my items to know if the item is selected, both in the paint and the sizeHint function.
QListView is derived from QAbstractItemView, which has a method to get the selection model:
QItemSelectionModel *selectionModel = myView->selectionModel();
This method returns a pointer to the selection model, which is long-lived, i.e., you can save the pointer, connect to its signals, etc.
The answer given by Daniel is correct, but it is better to show it with an example suitable for beginners:
class MyCustomModel : public QAbstractListModel
{
Q_OBJECT
public:
ImageCollectionModel(QObject *parent, MyCustomCollection *data);
: QObject(parent)
, m_myData(data)
{
}
public slots:
void onSelectedItemsChanged(QItemSelection selected, QItemSelection deselected)
{
// Here is where your model receives the notification on what items are currently
// selected and deselected
if (!selected.empty())
{
int index = selected.first().indexes().first().row();
emit mySelectedItemChanged(m_myData->at(index));
}
}
signals:
void mySelectedItemChanged(MyCustomItem item);
private:
MyCustomCollection *m_myData;
// QAbstractItemModel interface
public:
int rowCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &index, int role) const override;
};
When you pass your custom model to the QListView, that's a great opportunity to connect it:
ui->myListView->setModel(m_myModel);
connect(ui->myListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
m_myModel, SLOT(onSelectedItemsChanged(QItemSelection, QItemSelection)));
Using a QListView, and QStandardItemModel, is it possible to display icons in the list view without displaying the associated text? QStandardItem is defined as so:
QStandardItem ( const QIcon & icon, const QString & text )
So it seems to require a text string of some sort - I only want the icon displayed. If I use the following code, I get the icons as requested, but I also get a blank text element underneath them. I don't want this.
ImageListView->setViewMode( QListView::IconMode );
{
QStandardItemModel *iStandardModel = new QStandardItemModel(this);
QStandardItem* item1 = new QStandardItem(QIcon("images/shield-280x280.png"),"");
QStandardItem* item2 = new QStandardItem(QIcon("images/shield-280x280.png"),"");
iStandardModel->appendRow(item1);
iStandardModel->appendRow(item2);
ImageListView->setIconSize(QSize(100,100));
ImageListView->setUniformItemSizes(true);
ImageListView->setDragDropMode(QAbstractItemView::DropOnly);
ImageListView->setModel(iStandardModel);
}
If I go to the trouble of building a custom model, can I resolve this issue?
To expand on the accepted answer, here's the simplest delegate which can optionally hide the text (display role) of items, but otherwise acts like the default delegate. This works with any QAbstractItemView subclass (and QComboBox) and any QAbstractItemModel subclass as well. And is a better solution if one would rather keep the display role non-null for other views (or whatever reason).
class ItemDelegate : public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
// simple public member to toggle the display role (create getter/setter if you prefer)
bool displayRoleEnabled = false;
protected:
void initStyleOption(QStyleOptionViewItem *o, const QModelIndex &idx) const override
{
QStyledItemDelegate::initStyleOption(o, idx);
// to hide the display role all we need to do is remove the HasDisplay feature
if (!displayRoleEnabled)
o->features &= ~QStyleOptionViewItem::HasDisplay;
}
};
Yes, you can do.
first you create a delegate associated with the list-view.Then,
While inserting the elements to the listview, use set-data function to insert the icon and in the paint event of delegate you handle the drawing icon. i hope its clear.