Reediting a QTableView cell when the given value is not acceptable - qt

I have a QTableView that obtains the data from a custom model and it's edited using a custom delegate.
//...
view->setModel(stockModel);
view->setItemDelegateForColumn(0, nameDelegate);
When the user edits a specific cell it types some text (name for an object) and this text could be accepted by the program or not (the program doesn't want to have repeated names).
My solution for this was to the custom delegate to have a signal: notValidText(QModelIndex) and use the signal/slot mechanism to connect the signal to the tableview edit(QModelIndex) slot. This, from what i know, should reedit the cell in question:
//implementation of the delegate
void NameDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index)
{
QLineEdit *line = static_cast<QLineEdit*>(editor);
if(!model->setData(index, line->text(), Qt::EditRole))
{
emit notValidData(index);
}
}
//connection of the view with the delegate
connect(nameDelegate, SIGNAL(notValidData(QModelIndex)), view, SLOT(edit(QModelIndex)));
Unfortunately this is not what happens, so i'm doing something wrong. If someone could give me a tip i would really appreciate.
I just put here some code to show what i did, ask if you need me to post anymore.
Thanks in advance

Related

itemChanged() on QStandardItemModel triggered when data hasn't changed

I have the following connect line:
connect(my_QStandardItemModel ,SIGNAL(itemChanged(QStandardItem*)),
this,SLOT(cellEditEndedCalled(QStandardItem*)));
For some reason whenever I go into edit mode on a cell inside my table (double click) and click on another cell, cellEditEndedCalled() is being called even though I didn't make any changes to my data.
Any ideas on why this might be happening?
EDIT:
Tried with dataChanged(...) instead of itemChanged(...) but the slot is still being called.
Implementation of my_QStandardItemModel :
class my_QStandardItemModel :public QStandardItemModel
{
typedef QStandardItemModel baseClass;
Q_OBJECT
public:
my_QStandardItemModel ();
virtual ~my_QStandardItemModel ();
...
Not overwriting any signals afterwards.
Because the signal itemChanged is not the correct for your situation. QStandardItemModel inherits another singal form QAbstractItemModel
void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const
QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ())
that emits with QModelIndex information on the index where changes occured : your cell.
you need to connect that signal to your slot (to be modified to match new signal signature).
Why the itemChanged signal is emitted even you did not modify the data: because that signal emits when you change your item NOT the data in it.

Using QAbstractListModel in ListView

I'm new with Qt , so please bear with me .
I've successfully managed to populate a ListView from a StringList and a QList of Object*
What I'm struggling now with is to populate a ListView in QML using a class defined in C++ that derives QAbstractListModel.
Here's the prototype of my CPP class :
class MessageListEntryModel : public QAbstractListModel
{
Q_OBJECT
public:
enum eMLERoleTypes
{
MLERT_MSG = Qt::UserRole+1,
MLERT_COLOR
};
MessageListEntryModel(QObject* parent=0);
virtual ~MessageListEntryModel();
void AddEntry(QString aMessage, QColor aColor);
// pure virtuals implementations
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const ;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &child) const ;
QHash<int,QByteArray> roleNames();
private:
QList<MessageEntry*> m_vpMessages;
MessageEntry is a simple class that contains 2 memebers , a QColor and a QString (the class doesn't extend QObject).
I had to implement all the above functions since they're pure virtual in an underlying class (is this normal? so far in tutorials/samples people mentioned only about roleNames and data).
The implementation of roleNames and data are as following :
QHash<int,QByteArray> MessageListEntryModel::roleNames()
{
QHash<int,QByteArray> rez;
rez[MLERT_MSG]="message";
rez[MLERT_COLOR]="messagecolor";
return rez;
}
QVariant MessageListEntryModel::data(const QModelIndex &index, int role) const
{
qDebug()<<" Data asked for "<<index.row()<<" and role "<<role;
if (index.row()<0 || index.row()>=m_vpMessages.size())
{
return QVariant();
}
MessageEntry* entry = m_vpMessages[index.row()];
if (role == MLERT_MSG)
{
return QVariant::fromValue(entry->message);
} else if (role == MLERT_COLOR)
{
return QVariant::fromValue(entry->messageColor);
}
// should be unreachable code
return QVariant();
}
The QML portion of the List View is something like this :
ListView {
id: quickMessageListdata
model: quickListModel
delegate: Rectangle {
width: 400
height: 25
color:"#000000"
Text{
text: model.message
color: model.messagecolor
}
}
So far this is my understanding on how to implement things in CPP and QML.
For linking these two, I use the following code :
MessageListEntryModel* model =new MessageListEntryModel();
// Add various entries
...
// assign model in QML
m_pViewRef->rootContext()->setContextProperty("quickListModel",model);
With the code above, when running nothing is displayed in the ListView and I'm getting the following errors :
Unable to assign [undefined] to QString
Unable to assign [undefined] to QColor
I'm also registering the model class to be exported to QML (don't know if this is necessary) :
qmlRegisterType<MessageListEntryModel> ("dlti.exported",1,0,"MessageListEntryModel");
So it's quite obvious that either I missuderstood the proper use of a QAbstractListItem derived class OR I miss a simple vital key information.
I would appreciate some pointers to some relevant samples / tutorials (one that also shows you how to properly access data from the model in QML, since I've noticed that in CPP it never passes through the data function).
Also please notice that I'm using qt5 , so qt4.8 samples won't do the trick.
EDIT
After long hours of frustrations, I finally managed what was wrong with the damn thing :
My roleNames function signature was wrong !
The correct signature for overload is :
protected :
QHash<int,QByteArray> roleNames() const;
Please notice the protected and the const modifiers.
After declaring the function the correct way , it all worked fine.
For further notice, implementing data and rowCount was sufficient :).
Thanks for the help.
I will accept BaCaRoZzo's answer since I only manged to figure this out after looking over the code from the example.
As a side note, it works well with both message and model.message.
How do you implement the addition method? You should use a method like in the example provided in my comment.
From the docs:
An insertRows() implementation must call beginInsertRows() before
inserting new rows into the data structure, and it must call
endInsertRows() immediately afterwards.
You should have something like:
void MessageListEntryModel::add(params...)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount()); // kindly provided by superclass
// object creation based on params...
m_vpMessages << objectCreated;
endInsertRows(); // kindly provided by superclass
}
with
int MessageListEntryModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_vpMessages.count();
}
Also, #Jonathan Mee comment is correct: use just the role names inside the delegate as you defined them in the model. If everything else is correct that is the way to access data.
One of the best documentation piece to understand C++ models implementation and usage is the Model subclassing reference. In this document is clearly depicted which are the most important methods to subclass and for what purposes.
As for the methods to implement, it really depends on the needs. Depending on the possible actions on the model, different methods should be implemented (refer to the above link for full details). A model for a ListView in which items can be added/removed can inherit from QAbstractListModel and just rely on the default implementations for most of the functions. You just need data(), roleNames() and rowCount(), as you already saw in most examples.
If instead you also need to edit data not just adding/removing it, then you also need other functions, particularly setData(). It is also your duty to notify the attached view(s) with any modification of the model occurred in setData(), via the signal dataChanged(). Refer again to the above provide subclassing reference.
Note also that, if the add method is modified with the Q_INVOKABLE modifier, i.e. it is declared as
Q_INVOKABLE void add(params...);
in the model header, you can also call it from QML (since the model is set as a context property) and you can write, for instance:
ListView {
id: quickMessageListdata
model: quickListModel
delegate: Rectangle {
width: 400
height: 25
color:"#000000"
Text{
text: model.message
color: model.messagecolor
}
}
Component.onCompleted: {
quickListModel.add(params)
quickListModel.add(params)
}
}
to insert items in the view as soon as the view is created. Clearly the same approach can be applied to other QML signals so that you can react to QML events and trigger addition/removal behaviours.
Finally, you don't need to register the model with qmlRegisterType. For your current requirement it is superfluous.
Hmmm... I'm not super familiar with QML but I believe that this is your problem: http://qt-project.org/doc/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel
According to the link it looks like you need to change your Text block to:
Text{
text: message
color: messagecolor
}
Further reading: http://qt-project.org/forums/viewthread/5491
I've also experienced hard times before when creating own list models with Qt C++. To avoid the development overhead for a C++ model, I started to use QSyncable (a existing QAbstractListModel implementation by Ben Lau). You can find it on GitHub here.
The best part of the project is the JsonListModel QML type. It can transform any variant JSON list that you create or fetch in QML to a full-featured QML ListModel. This saves a lot of time and effort for applications that e.g. work with JSON or REST services. You can find a detailed guide how it works here.

Displaying a tooltip for QTreeWidgetItem when it's hovered without calling setTooltip() for every item

I want to display a tooltip for QTreeWidgetItem that's hovered. However, getting a tooltip is not a very fast process in my case, so I don't want to call setTooltip() for every single item. I want to do it on demand, on some event or signal. What's the easiest way to do it?
The best solution I've found is to subclass QTreeWidgetItem, override virtual QVariant data(int column, int role) const; and return a tooltip for this item when data is called for Qt::ToolTipRole.
I think that it should be easier to achieve what you want if you migrate to a QTreeView/Model pattern.
QAbstractItemModel has a role for tooltips: Qt::ToolTipRole
You could subclass a Model to reimplement the
QVariant QAbstractItemModel::data ( const QModelIndex & index, int role = Qt::DisplayRole ) const [pure virtual
method.
So, when receives a Qt::TooltipRole, it calculates/recovers from an internal cache.

QTableView cell data disapears when the cell is activated

I'm developing an application using QT5.0 and new to QT. Badly, i have not too much time for a long learning curve.
I have derived my own TableModel and set it to a editable TableView. TableView shows model's data, it works. But when i activate a cell on the tableview, the data disappears. I looked at the documentation and saw that QTableView is derived from QAbstractItemView class which have a signal called 'activated' and a slot called 'edit'. So, i think 'activated' signal is connected to 'edit' slot. But 'edit' is not virtual, so i can not override it. I may connect my child class to parents 'activated' signal but actually i do not know how to handle this signal in order to save the current data of the TableView object.
There is no problem if the code uses SqlTableModel. I think it handles the 'activated' signal but I'm not sure about these, just speculating..
What is the right way to do this?
Check your the data function:
QVariant TableModel::data(const QModelIndex &index, int role) const
if( !index.isValid() )
return QVariant();
if( role == Qt::DisplayRole || role == Qt::EditRole) {
return <your data>
}
return QVariant();
}
Ensure that you process the EditRole role.
Good luck!

Using a delegate with a QDataWidgetMapper and QLabel

I'm trying to use a delegate to customize the way data from a model is displayed when using a QDataWidgetMapper.
I have two different versions of a widget, one is view-only (the data is displayed in QLabels) and the other is used to edit the data (the data is displayed in appropriate editors).
The latter one works perfectly with the delegate, everything is fine.
As you may have guessed the problem arises with the first one... When mapping the sections of my model to QLabels using the QDataWidgetMapper, the delegate is never called and the data is displayed correctly for the sections with regular data (strings, ints,...) but no data is displayed for the sections of my model with a custom data type (a kind of list) which I would like to format as a string using the delegate.
I've already performed this operation successfully when the same data is displayed in a QTableView (the method paint() of the delegate is called when the data is displayed).
After having looked at it a little bit closer, I've been able to see that, when using QLabels to display the data, the delegate is never called though I've explicitly associated a delegate to the QDataWidgetMapper using its method setItemDelegate().
So in synthesis, assume a class CustomItemDelegate which inherits QStyledItemDelegate with virtual methods:
void CustomItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
qDebug() << "DELEGATE: PAINT" << index.column();
QStyledItemDelegate::paint(painter, option, index);
}
void CustomItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
qDebug() << "DELEGATE: SET EDITOR DATA" << index.column();
QStyledItemDelegate::setEditorData(editor, index);
}
and a widget with following in it:
QDataWidgetMapper* mapper = new QDataWidgetMapper();
CustomItemDelegate* delegate = new CustomItemDelegate();
mapper->setModel(model);
mapper->setItemDelegate(delegate);
mapper->addMapping(editorWidget, 1);
mapper->addMapping(label, 2, "text");
mapper->toFirst();
QTableView* view = new QTableView();
CustomItemDelegate* delegate2 = new CustomItemDelegate();
view->setModel(model);
view->setItemDelegate(delegate2);
the code outputs:
DELEGATE: SET EDITOR DATA 1
// NOTHING ?!
DELEGATE: PAINT 1
DELEGATE: PAINT 2
and as a result I got
my editorWidget with the correct data in it (whatever data type the section contains: regular or custom, as long as the editor handles the type of course),
my label only displays the data if the section contains a regular type of the data as the delegate is not called
my view would display everything fine as the delegate is called for each section
So my questions are:
why isn't the delegate called when the mapped widget is a QLabel?
in this case, how come the data is even displayed when the data type is regular? Magic?
Thanks very much and I apologize in advance if the answer is obvious (but even then, thank you for pointing it out :P),
ixM
This is the code from QT that populates widgets
void QDataWidgetMapperPrivate::populate(WidgetMapper &m)
{
if (m.widget.isNull())
return;
m.currentIndex = indexAt(m.section);
if (m.property.isEmpty())
delegate->setEditorData(m.widget, m.currentIndex);
else
m.widget->setProperty(m.property, m.currentIndex.data(Qt::EditRole));
}
In the first case when you do not specify a property delegate is used whereas in the second case the data is set to widget directly by passing your delegate.
I don't know why it was designed this way but this is how it works currently !

Resources