QTableView: change precision for double values - qt

If a double value returned as EditRole by a model, then (supposedly) QDoubleSpinBox is used by QTableView as an editor. How can I change precision in that control?

The precision behavior of QDoubleSpinBox in a QTableView is explained here , so to solve the problem , you need to set your own QDoubleSpinBox, there are two ways to do this according to the end part of Subclassing QStyledItemDelegate:
using an editor item factory or subclassing QStyledItemDelegate. The latter way requires you to reimplement four methods of QStyledItemDelegate, I just fount it a bit lengthy, so I choose the first way , sample code in PyQt following :
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ItemEditorFactory(QItemEditorFactory): # http://doc.qt.io/qt-5/qstyleditemdelegate.html#subclassing-qstyleditemdelegate It is possible for a custom delegate to provide editors without the use of an editor item factory. In this case, the following virtual functions must be reimplemented:
def __init__(self):
super().__init__()
def createEditor(self, userType, parent):
if userType == QVariant.Double:
doubleSpinBox = QDoubleSpinBox(parent)
doubleSpinBox.setDecimals(3)
doubleSpinBox.setMaximum(1000) # The default maximum value is 99.99.所以要设置一下
return doubleSpinBox
else:
return super().createEditor(userType, parent)
styledItemDelegate=QStyledItemDelegate()
styledItemDelegate.setItemEditorFactory(ItemEditorFactory())
self.tableView.setItemDelegate(styledItemDelegate)
self.tableView.setModel(self.sqlTableModel)

I have not been able to find a good way to get at those spin boxes. The default delegate for a QTableView is a QStyledItemDelegate. When creating a item in Qt::EditRole it uses items created by the default QItemEditorFactory class, which you can access using QItemEditorFactory::defaultFactory(). You could then register your own editor there, however I do not see a good way to edit the ones that are already there.
Instead most likely what you should do is implement your own delegate with a different precision specified. There is a example to make a delegate using a QSpinBox, which you would replace with a QDoubleSpinBox. Then in createEditor you would then use setDecimals to set the spin box to the precision that you want. You then apply that delegate to your table with setItemDelegate.

According to the documentation, the precision of QDoubleSpinBox can be changed by calling decimals.

Here is a minimal QStyledItemDelegate implementation that modifies the QDoubleSpinBox precision:
const int DOUBLESP_PRECISION = 6;
class SpinBoxDelegate : public QStyledItemDelegate
{
public:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE
{
auto w = QStyledItemDelegate::createEditor(
parent, option, index);
auto sp = qobject_cast<QDoubleSpinBox*>(w);
if (sp)
{
sp->setDecimals(DOUBLESP_PRECISION);
}
return w;
}
};

Related

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.

Custom Sorting in QTableWidget

I have a QTableWidget and i am using its default sorting capability through header columns but one of my column in QTableWidget is integer type and through QTableWidget default sorting it is being sorted like a string.So there is any means by which i can use my own sorting functions for QTableWidget?
You can try to subclass the QTableWidgetItem and reimplement operator<() of it. Than in your QTableWidget use this custom items instead of default QTableWidgetItems. Something like this:
class Item: public QTableWidgetItem
{
public:
[..]
bool operator< (const QTableWidgetItem &other) const
{
// TODO: To be safe, check weather conversion to int is possible.
return (this->text().toInt() < other.text().toInt());
}
[..]
};
And in your table widget:
[..]
QTableWidgetItem *newItem = new Item("1");
tableWidget->setItem(row, column, newItem);
[..]
I am not sure, but I don't think there is an easy way to change the sorting behaviour of a QTableWidget.
QTableWidget is just a convenience class for QTableView, which uses a default model. No guarantee, but what would try to do:
QTableWidget inheris the model() method from QTableView. With it you should be able to get the widget's model:
QAbstractItemModel *model = yourTableWidget->model();
This was the easy part. You now need a custom QSortFilterProxyModel, where you can override the virtual bool lessThan(const QModelIndex & left, const QModelIndex & right) const method.
And finally:
YourCustomFilterProxyModel *proxyModel = new YourCustomFilterProxyModel(this);
proxyModel->setSourceModel(model);
yourTableWidget->setModel(proxyModel);
No guarantee in so far that I never tried to replace the default model in a QTableWidget. If possible you should look into the Qt views and models. Initially they look harder to use, but it pays to get comfortable with them. IMHO QTableWidget is just an ancient relict from Qt3.
Are you sure it didn't sort the data well? Make sure you do add number there, not string. So, to add 40 to QTableWidget row, you use this data:
36: {'firstname': 'b', 'lastname': '111', 'email': 'foo#gmail.com',
'affiliate': 'Stuart Little', 'total_account_value': 40},
Instead of this:
36: {'firstname': 'b', 'lastname': '111', 'email': 'foo#gmail.com',
'affiliate': 'Stuart Little', 'total_account_value': '40'},
QTableWidget will recognize it as integer and will sort it well

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.

Exposing child's properties in the parent

I want to create a custom widget that contains a QSpinBox. I want the custom widget to expose some of QSpinBox's properties as its own so it can work conveniently in Designer.
Is there a convenient way to do this kind of property proxying in Qt?
I want to reiterate that the relationship between my custom widget and the QSpinBox is containment, not inheritance. I am using pyqt, but will happily accept a pure qt answer.
To clarify, I want to create variations of standard widgets that have some extra decorations. For example, I will be adding a toggleable icon to sit beside my QSpinBox. However I still want to be able to configure the QSpinBox in designer (ie, set the suffix, upper and lower limits, increment, etc).
Alrite, so here's a way to do it.
It's better than writing a function manually for each property. Basically, you write a macro that expands the code for you.
I have a doubleSpinBox in my widget and its value is changed whenever I change the property. And you emit a signal whenever the property changes, which is connected to the doubleSpinBox->setValue(). Tested and works perfectly.
Complete code at this link.
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtGui/QWidget>
#include <QtGui/QLabel>
#include "ui_mywidgetform.h"
#define MYPROPERTY_SET_FUNC(_PROPCLASS_, _PROP_PARAM_NAME_, _PROP_SET_FUNC_NAME_, _PROP_NOTIFY_) \
void _PROP_SET_FUNC_NAME_(_PROPCLASS_ _PROP_PARAM_NAME_) \
{ \
emit _PROP_NOTIFY_(_PROP_PARAM_NAME_); \
}
#define MYPROPERTY_GET_FUNC(_PROPCLASS_, _PROP_READ_FUNC_NAME_, _PROP_VARIABLE_)\
_PROPCLASS_ _PROP_READ_FUNC_NAME_() const \
{ return _PROP_VARIABLE_; }
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
~MyWidget();
enum Accuracy {Good, Bad};
signals:
void doubleSpinBoxValueChanged(double);
void accuracyChanged(Accuracy);
private:
Q_PROPERTY(Accuracy accuracy READ accuracy WRITE setAccuracy NOTIFY accuracyChanged)
Q_PROPERTY(double doubleSpinBoxValue READ doubleSpinBoxValue WRITE setDoubleSpinBoxValue)
private:
Accuracy m_accuracy; //custom class property
Ui::Form m_ui;
public:
//Getter functions
MYPROPERTY_GET_FUNC (double, doubleSpinBoxValue, m_ui.doubleSpinBox->value())
MYPROPERTY_GET_FUNC (Accuracy, accuracy, m_accuracy)
//Setter functions
MYPROPERTY_SET_FUNC(Accuracy, accuracy, setAccuracy, accuracyChanged)
MYPROPERTY_SET_FUNC(double, doubleSpinBoxValue, setDoubleSpinBoxValue, doubleSpinBoxValueChanged)
};
#endif // MYWIDGET_H_
Here is a PyQt version inspired by blueskin. The main operational difference here is that the custom widget class is composed at runtime rather than compile time. The advantage to this is that we can programmatically inspect the target metaobject and use that to generate the proxy properties.
Now PyQt is compiled from c++, so I have to believe that it is also possible to generate qt types at runtime in c++. I am betting that would be hellish, however.
from PyQt4 import QtGui, QtCore
def makeProxyProperty(childname, childtype, childpropname):
metaobject = childtype.staticMetaObject
metaproperty = metaobject.property(metaobject.indexOfProperty(childpropname))
def getter(self):
return metaproperty.read(getattr(self, childname))
def setter(self, val):
return metaproperty.write(getattr(self, childname), val)
return QtCore.pyqtProperty(metaproperty.typeName(), fget=getter, fset=setter)
class Widget1(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.spinbox = QtGui.QSpinBox()
self.checkbox = QtGui.QCheckBox()
layout = QtGui.QHBoxLayout()
layout.addWidget(self.checkbox)
layout.addWidget(self.spinbox)
self.setLayout(layout)
spinbox_suffix = makeProxyProperty("spinbox", QtGui.QSpinBox, "suffix")
spinbox_prefix = makeProxyProperty("spinbox", QtGui.QSpinBox, "prefix")
spinbox_minimum = makeProxyProperty("spinbox", QtGui.QSpinBox, "minimum")
spinbox_maximum = makeProxyProperty("spinbox", QtGui.QSpinBox, "maximum")
So to make things a bit clear,
you will be using this custom widget in the designer.
You need the custom widget to have properties that expose QSpinBox properties as the widget's properties
So,
basically you would want to create a property in your custom widget
using 'Q_PROPERTY' and use setter and getter methods to change the
QSpinBox's properties I hope this example would help
http://doc.qt.nokia.com/latest/properties.html#a-simple-example

QT: trouble with qobject_cast

I have derived QGraphicsItem and QGraphicsScene classes. I want the items to be able to call scene() and get a derviedGraphicsItem * instead of a QGraphicsItem *, so I reimplemented QGraphicsScene::itemAt to return a derived pointer.
DerivedItem* DerivedScene::itemAt( const QPointF &position, const QTransform &dt ) const
{
return qobject_cast< DerivedItem * >(
QGraphicsScene::itemAt(position, dt) );
}
I get the following error (Qt 4.6, GCC 4.4.3 on Ubuntut 10.4)
scene.cpp: In member function ‘DerivedItem* DerivedScene::itemAt(qreal, qreal, const QTransform&) const’:
scene.cpp:28: error: no matching function for call to ‘qobject_cast(QGraphicsItem*)’
I then noticed QGraphicsItem doesn't inherit QObject, so I made my derived QGraphicsItem class have multiple inheritance from QObject and QGraphicsItem, and after adding the Q_OBJECT macro and rebuilding the project I get the same error.
Am I going about this the wrong way? I know it's supposed to be bad design to try to cast a parent class as a child, but in this case it seems like what I want, since my derived item class has new functionality and its objects need a way to call that new functionality on items around themselves, and asking the items scene object with itemAt() seems like the best way - but I need itemAt() to return a pointer of the right type. I can get around this by having the derived items cast the QGraphicsItem * returned by QGraphicsScene::itemAt() using dynamic_cast, but I don't really understand why that works and not qobject_cast, or the benefits or disadvantages to using dynamic_cast vs. qobject_cast.
EDIT:
forgot to mention that I also reimplemented QGraphicsItem::scene() in my derived class to return a DerivedScene *, as
DerivedScene* DerivedItem::scene() const
{
return qobject_cast< DerivedScene * >( QGraphicsItem::scene() );
}
but this doesn't appear to be causing a compilation error...
There is no point in inheriting from QObject just for casting. The advantage of qobject_cast
over dynamic cast is summed up pretty much in the qobject_cast documentation:
The qobject_cast() function behaves similarly to the standard C++ dynamic_cast(), with the advantages that it doesn't require RTTI support and it works across dynamic library boundaries.
It's nice to have and useful if you have QObjects, but not worth to inherit from QObject if it is all you want from QObject.
Also, for QGraphicsIems there is qgraphicsitem_cast, which should do exactly what you want :)
You have to pass a QObject pointer to qobject_cast() and QGraphicsScene::itemAt returns a QGraphicsItem pointer. Since, as you mentioned, QGraphicsItem does not derive from QObject, the complier gave you that error.

Resources