QTreeView Edit UserRole Instead of DisplayRole Upon Double Click - c++

In my project, I have a QTreeView displaying items from a QStandardItemModel. Each item has data stored in several UserRoles.
QStandardItem* item = new QStandardItem();
item->setIcon(iconByte);
item->setData(3, Qt::UserRole+1);
item->setData(name, Qt::UserRole+2);
item->setData(data, Qt::UserRole+3);
... and so on
When the user double clicks on an item, a dialog with two line edits displays allowing the user to edit parts of the UserRole data. When editing ceases, the edits run through some logic and a display name is generated based on the new UserRole data.
However, this gets very tedious very quickly. With dialogs constantly popping up and whatnot, it's a slow and ugly solution.
I now would like to remove the dialog completely and show the line edit widgets WITHIN the item itself. By default, double clicking an item to edit it only shows one line edit widget to change the DISPLAY role. However I want two line edits to change the two USER roles. And then the normal logic continues.
How would I go about modifying the edit item portion of a QTreeView?
Thanks for your time!

I would use a custom subclass of QStyledItemDelegate to solve this. Somewhere near your QTreeView you could have a QComboBox switching between the user roles; your custom delegate would somehow be informed which user role is currently selected and would intercept the method updating the data in the model to set the proper role.
An example implementation (not tested, may contain typos and errors):
class RoleSwitchingDelegate: public QStyledItemDelegate
{
public:
explicit RoleSwitchingDelegate(QComboBox * roleSwitcher, QObject * parent = 0);
virtual void setEditorData(QWidget * editor, const QModelIndex & index) const Q_DECL_OVERRIDE;
virtual void setModelData(QWidget * editor, QAbstractItemModel * model,
const QModelIndex & index) const Q_DECL_OVERRIDE;
private:
QComboBox * m_roleSwitcher;
};
RoleSwitchingDelegate::RoleSwitchingDelegate(QComboBox * roleSwitcher, QObject * parent) :
QItemDelegate(parent),
m_roleSwitcher(roleSwitcher)
{}
void RoleSwitchingDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
{
// Assuming the model stores strings for both roles so that the editor is QLineEdit
QLineEdit * lineEdit = qobject_cast<QLineEdit*>(editor);
if (!lineEdit) {
// Whoops, looks like the assumption is wrong, fallback to the default implementation
QStyledItemDelegate::setEditorData(editor, index);
return;
}
int role = m_roleSwitcher->currentIndex();
QString data = index.model()->data(index, role).toString();
lineEdit->setText(data);
}
void RoleSwitchingDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
// Again, assuming the model stores strings for both roles so that the editor is QLineEdit
QLineEdit * lineEdit = qobject_cast<QLineEdit*>(editor);
if (!lineEdit) {
// Whoops, looks like the assumption is wrong, fallback to the default implementation
QStyledItemDelegate::setModelData(editor, model, index);
return;
}
int role = m_roleSwitcher->currentIndex();
QString data = lineEdit->text();
model->setData(index, data, role);
}
Once you have the delegate, you just need to set it to the view:
view->setItemDelegate(new RoleSwitchingDelegate(roleSwitchingComboBox, view));

Related

Qt set a custom widget inside a QTableView

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.

QTableWidget: Only numbers permitted

Is there any way to disallow any characters except numbers (0-9) in a QTableWidget? For QLineEdits I'm using a RegEx validator, but this is not available for QTableWidgets. I thought of inserting QLineEdits in as CellWidgets into the table, but then I had to rewrite an extreme large amount of functions in my code. So, is there any other (direct) way to do so?
I would suggest using an item delegate for your table widget to handle the possible user input. Below is a simplified solution.
The implementation of item delegate:
class Delegate : public QItemDelegate
{
public:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
QLineEdit *lineEdit = new QLineEdit(parent);
// Set validator
QIntValidator *validator = new QIntValidator(0, 9, lineEdit);
lineEdit->setValidator(validator);
return lineEdit;
}
};
Implementation of the simple table widget with the custom item delegate:
QTableWidget tw;
tw.setItemDelegate(new Delegate);
// Add table cells...
tw.show();

QListView & QStandardItemModel check text before editing row

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

How do I detect row selections in QListView <- > QAbstractListModel with Item Delegate?

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

Is there a way to display icons in QListView without text?

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.