I'm writing a Qt application to allow the visualization of very heavy data sets.
So, I have a SourceDataModel class, inheriting from QAbstractItemModel that seems to work properly (currently, I only display it in QTableView/QTreeView but later on, I'll create some custom views).
Now, I would like to be able to filter this data, that is
being able to have different data
resolution (i.e. only exposing 1
data item out of 2)
being able to
apply some filters on the data (i.e.
displaying unix timestamps as
dd/MM/yyyy hh:mm:ss)
So I started to create a ProxySourceDataModel class, which inherits from my SourceDataModel and stores one instance, and basically delegates everything to the instance. From this ProxySourceDataModel, I noticed that no data was displayed when I used it in a QTableView. After some investigation, it seems that it was because i had to forward the signals and slots from the underlying SourceDataModel. No problem, i did it.
But still 2 problems remain, and I can't figure out how to handle them:
I am not able to select the data in the views. If I use the SourceDataModel directly, no problem. But using the ProxySourceDataModel i can't select anything.
The data is not filtered at all! I overloaded data() in ProxySourceDataModel, and forward all the other calls to the underlying SourceDataModel. But still, only SourceDataModel::data() is called.
Here is some code to illustrate what I'm doing:
class SourceDataModel : public QAbstractItemModel
{
//...
};
class ProxySourceDataModel : public SourceDataModel
{
public:
ProxySourceDataModel(SourceDataModel& model)
: model_(model)
{
// For all QAbstractItemModel's signals emitted by the underlying model,
// I propagate them like this
QObject::connect( &model_, SIGNAL( the_signal()),
this, SLOT (forward_the_signal())) ;
}
slots:
void forward_the_signal()
{
emit the_signal();
}
public:
// For all QAbstractItemModel's virtual function, I do something like this
virtual void the_function()
{
model_.the_function();
}
// This is where I was hoping to do the filtering
virtual QVariant data( const QModelIndex& index,int role=Qt::DisplayRole )
{
return filter( model_.data(index,role) );
}
private:
SourceDataModel& model_;
};
SourceDataModel sourceDataModel;
QTableView view;
view.setModel( new ProxySourceDataModel(sourceDataModel) );
Any help or advice greatly appreciated, thanks for reading!
-------------------- EDIT ------------------------
I found it!
The problem was that the view do not use QAbstractItemModel::data() from its model, but rather calls QModelIndex::data() on its items, which in turn calls the QAbstractItemModel::data() of the item's underlying model.
And since my proxy returned model indexes from the underlying model, that is why the SourceDataModel::data() was always called instead of ProxySourceDataModel()!
I just reimplemented ProxySourceDataModel::index() to return local indexes, and it works like a charm.
Visit In QT, chaining models does not work as expected for more informations.
Thanks!
The problem was that the view do not use QAbstractItemModel::data() from its model, but rather calls QModelIndex::data() on its items, which in turn calls the QAbstractItemModel::data() of the item's underlying model.
And since my proxy returned model indexes from the underlying model, that is why the SourceDataModel::data() was always called instead of ProxySourceDataModel()!
I just reimplemented ProxySourceDataModel::index() to return local indexes, and it works like a charm.
Visit In QT, chaining models does not work as expected for more informations.
Related
I would like to use call a C++ function from Qml in the following way:
Item{
id: qmlItem
function loadModel(dataModel)
{
// setup my listview
}
MyDataModel{
id: model
}
Button{
onClicked: {
cpp.addValues(model)
loadModel(model)
}
}
}
Now in C++ I have the following function:
void myCpp::addValues(MyDataModel* model)
{
// here I would like to add items to my data model
model->addItem();
}
Now this would be possible if the model was passed as a pointer, but I am not sure if this is the behaviour of C++/Qml interaction. Would what I wrote work or there is another approach I need to use?
EDIT: I have compiled the program and it works fine. However, I would like to know what is going under the hood. I mean does QML sends parameters by pointer? Would the same approach work with a JavaScript array (instead of MyDataModel)?
Actually, if you want to use model for ListView which is passed from C++ you should look at the QAbstractItemModel or QAbstractListModel(maybe better and easier for your case) class from which you inherit and create your own model to use in the qml. To subclass QAbstractListModel from you'll at least need to reimplement rowCount() , data() methods, also you can reimplement insertRows() and removeRows() methods to dynamically add/remove data from model (which will also update the view automatically).
You should look up the http://doc.qt.io/qt-4.8/qabstractlistmodel.html or http://doc.qt.io/qt-4.8/qabstractitemmodel.html
. Also, there were some easy examples in qtcreator on how to implement this kind of model
I am accessing a MySQL 5.6 database using Qt 5.3.1 SQL module. Currently I try to move some of that code from the main thread to a custom thread to allow the GUI thread to stay responsive during DB updates.
I understood that everything (including establishing of the connection) must be moved to the custom thread. I am using queued signals and slots to achieve this and it works properly.
However there is one thing I am not sure about: How can I return query results back to main thread? Of course I will use a signal for that. But what kind of object should I return in that signal?
Should I return the QSqlQuery? I suppose this will be dangerous since QSqlQuery is attached to the connection/database in some way.
Should I return a list of QSqlRecord objects taken from the query using record()? Unfortunately the documentation does not say a word if this is safe.
What is the right container/way to safely return the results?
If, for example, the database contained personal details, you could create a separate class, derived from QObject: -
class Person : public QObject
{
Q_OBJECT
public:
Person();
private:
QString m_firstName;
QString m_surname;
QString m_address
QDateTime m_dateOfBirth;
};
Then, having registered its metadata for using with signals and slots, retrieve the database record, populate the Person object and send it with signals and slots. The classes you create can then represent the tables in the database.
However, a much simpler method would be to use a QMap and emit a signal with that instead: -
QMap personMap;
personMap["name"] = sqlRecord.value().toString("name");
personMap["surname"] = sqlRecord.value().toString("surname");
personMap["address"] = sqlRecord.value().toString("address");
...etc
It may be a good idea to emit a function that takes a token and the map, where the token denotes the type of information that the map contains:-
emit RetrievedData("Person", personMap);
I would avoid sending the SqlRecord or anything to do with the underlying method of storing the data. It's always good to use loosely coupled classes. This way, you could decide to replace the database storage with another mechanism, without having to refactor all the other code.
----------- In response to comments ------------
Populate a map with the sql record. For simplicity, we assume all returned items are strings.
If record items are numbers, simply convert to string before storing in the map.
QMap PopulateMap(SQLRecord& sqlRecord)
{
QMap map;
for(int i=0; i<sqlRecord.count(); ++i)
{
map[sqlRecord.fieldName(i)] = sqlRecord.value(i).toString();
}
return map;
}
Considering a normal Model class defined by just implementing pure functions from QAbstractItemModel.
I have to display the model with a pie chart (like in this example) and I thought of derive from QAbstractItemView and just reimplement its protected slots.
The fact is that QAbstractItemView force me to implement other pure virtual functions:
virtual QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers);
virtual int horizontalOffset() const;
virtual int verticalOffset() const;
virtual bool isIndexHidden(const QModelIndex&) const;
virtual void setSelection(const QRect&, QItemSelectionModel::SelectionFlags);
virtual QRegion visualRegionForSelection(const QItemSelection&) const;
virtual QRect visualRect (const QModelIndex&) const;
virtual void scrollTo(const QModelIndex&, ScrollHint);
virtual QModelIndex indexAt(const QPoint&) const;
Most of this functions are useless in my case and as of right now I am returning 0, default values (for example return QRect();) or invalid values (for example return QModelIndex()).
My question is: what are these functions supposed to be used for (yes I RTFM but it barely tells you what values you should return)? Can I just ignore them, the way I am doing, and not expect repercussions?
Since you're the user of your own view, it's entirely up to you. If you don't feel like having a fully functional view, it's OK. The functions are used for selection and keyboard/mouse interaction with the items in the view. It's fine not to provide that functionality if you don't need it.
In many of read-only views that don't support interaction other than perhaps scrolling, deriving from a QAbstractItemView is rather pointless. I have an internal class I use instead. IMHO, Qt should further split the QAbstractItemView class so that interaction-free views would use something simpler.
You may be reassured by the fact that Qt itself provides lots of views that don't implement QAbstractItemView. Like every QML view :)
The minimum requirement for a "view" is that it derives from QObject. You can have non-gui "views" after all, that consume data from some model. Proxy models are such: they are gui-less views that attach to a model, and at the same time are a model themselves. To the source model, a proxy is like a view. To the user view, a proxy is like a model.
The source code relating to this question is available on my public Git repository on BitBucket.
I'm trying to dynamically add some items to a QTreeView model using the following code in mainwindow.cpp:
if(dlg->exec() == QDialog::Accepted) {
QList<QVariant> qList;
qList << item.name << "1111 0000" << "0x00";
HidDescriptorTreeItem *item1 = new HidDescriptorTreeItem(qList, hidDescriptorTreeModel->root());
hidDescriptorTreeModel->root()->appendChild(item1);
}
This works when run from within my MainWindow constructor, just after ui->setupUi(this), but I need this to run from within an event filter, but the same code doesn't get the QTreeView updating. When I set a breakpoint at mainwindow.cpp:70 and step through the next few lines, I can see the data is being added to the Model, but I need the QTreeView to refresh.
I understand this is done by emitting dataChanged(), but not really sure how to do this. The signal signature for the dataChanged signal looks as follows:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>());
so I need to come up with topLeft and bottomRight QModelIndex instances. How do I build/obtain these from item1 in the above snippet?
Also, where does beginInsertRows() and endInsertRows() come into view with this, should I be calling these functions?
From QAbstractItemModel documentation:
void QAbstractItemModel::beginInsertRows ( const QModelIndex & parent, int first, int last ) [protected]
Begins a row insertion operation.
When reimplementing insertRows() in a subclass, you must call this function before inserting data into the model's underlying data store.
The parent index corresponds to the parent into which the new rows are inserted; first and last are the row numbers that the new rows will have after they have been inserted.
The other protected functions say similar things.
And insertRows() says:
If you implement your own model, you can reimplement this function if
you want to support insertions. Alternatively, you can provide your
own API for altering the data. In either case, you will need to call
beginInsertRows() and endInsertRows() to notify other components that
the model has changed.
Take a look to QAbstractItemModel protected functions and signals
Views connect to those signals to know when model data changes and rearrange data inside. The functions emit the signals internally to make it easy for you to warn the view when it has happenned. But signals can only be emitted by abstract class.
Components connected to this signal use it to adapt to changes in the
model's dimensions. It can only be emitted by the QAbstractItemModel
implementation, and cannot be explicitly emitted in subclass code.
So you will have to stick to the methods.
Edit in answer to your comment:
Indeed, Items should have a reference to model and tell it about changes, check theses lines from QStandardItem:
void QStandardItem::emitDataChanged()
void QStandardItem::removeRows(int row, int count)
( Note, how, in second, it calls model's rowsAboutToBeRemoved() and rowsRemoved() )
Maybe you should try to use QStandardItem and QStandardItemModel.
Either direct or subclassing. It will hide a lot of ugly stuff.
There's also a less proper but much easier way to achieve this - emit layoutChanged() instead of dataChanged(). More info - https://stackoverflow.com/a/41536459/635693
I don't often have to create GUI's but today I do so I was hoping for some design input.
Basically I have a backend which I intend to add a GUI too using the MVC pattern. The issue is I feel whatever class encapsulates the main GUI window is going to have A LOT of state (all of the sub elements); and on top of that it's going to have a lot of setters, and possibly getter, clear, colour, size, position and refresh functions too.
One option is to march ahead with this idea and have a very large public interface which deals with the types the GUI uses (std::string, std::vector<std::string>...) the more control I want over the UI the more public member function I am going to need.
The other option would be to pass the program state to the GUI and have it decide how it display it, I fear doing this would mean it would give me less fine detail control and would break down the separation of concerns and would mean any changes to the representation of the program state would require changes in the GUI too.
Any input on the matter would be of great help.
If it makes any difference this is a C++ gui using an ncurses abstraction.
It sounds like to me you've thought alot about the M and the V, but not much about the C. The pattern should really be called MCV because the whole idea is that the controller IS the bridge between your model (data) and view (GUI). It sounds like you need a controller with most of the functionality you've mentioned.
Simply put though, your model obviously should know nothing about display and your display (view) should not know how to access the model. You need a controller that reads the data (model) and gives instructions to the display (view). If you have user interaction within the view, the controller can interpret that and modify the model as necessary.
The idea is that you never have to change all 3, but if you change the model or the view, you almost always have to update the controller.
Hope that helps...
There is at least one alternative to the giant interface. Instead of having a function that handles each thing (size, font, color, what-to-display, etc...) have a singular function that accepts a "role" and data that represents the role. This requires some sort of wrapper that can contain multiple data types.
QT's QAbstractItemModel Class Reference has a good example:
QVariant QAbstractItemModel::data ( const QModelIndex & index, int
role = Qt::DisplayRole ) const [pure virtual]
What that function will do is return the QVariant that represents the role indicated at the index provided.
The downside of this approach, is you have to know what roles exist, and what they do. QT's default roles are shown here.
I like to have parts of the model able to instrument themselves:
class Model {
private:
int value;
public:
void instrument(Instrumenter& instrumenter);
};
The Instrumenter manages the creation of controls. The model will tell it how it can be controlled and give it access to the data.
void Model::instrument(Instrumenter& instrumenter) {
instrumenter.addRangeController(0, 100, 5, value);
}
Then for different input devices (e.g keyboard, touchscreen) you can create appropriate controls:
class KeyboardInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create 3 widgets, up arrow, down arrow, and value
}
};
class TouchscreenInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create slider with min, max and increments
}
};
Instead of passing in the int directly we could have wrapped it in a controlling object, to aid encapsulation.