Returning a QSqlQuery result from a QThread - c++

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

Related

Passing QObject pointer from a QML object to C++

I have a publisher/subscriber key-value DB class in Qt/C++. The subscribers can connect by passing the key ( string ) , their QObject pointer and the property.
Whenever a value of the subscribed key changes, the properties of the subscribed QObject changes to the new value. Works in Qt/C++ fine.
Now I want to make a view in QML. Is it possible to pass from QML to C++ an object with 3 parameters:
QObject pointer of the QML object
property as string
DB-key as string
?
The preferable solution were, as if the property connects to another property:
Item{ myQmlProp: MyCppInst("myDBKey") }
EDIT
What currently works is this solution:
Item{
id:myqmlitem
myQmlProp: MyCppInst("myDBKey","myQmlProp",myqmlitem)
}
or like this:
Item{
id:myqmlitem
Component.onCompleted:{
MyCppPublisher.subscribe("myDBKey1","myQmlProp1",myqmlitem)
MyCppPublisher.subscribe("myDBKey2","myQmlProp2",myqmlitem)
}
}
Compared to the preferable solution, I have to pass the connected property name and the QML item instance explicitly. But it is ok, many thanks for the answers!
I've hoped to use QML's this-Keyword but have learned, that it is currently undefined in QML :-(
Just give the object an id and pass that id to the function, it will become a QObject * on the C++ side. Then you can use the meta system to access properties by name:
// qml
Item {
id: someitem
...
CppObj.cppFoo(someitem)
}
// c++
void cppFoo(QObject * obj) {
...obj->property("myDBKey")...
}
A reference would do as well, for example children[index].
What you could do is a function taking just your dbkey as a parameter, and return a QObject* exposing a Q_PROPERTY with a READ function and NOTIFY signal.
This way, you just have to tell with the notify signal the value has changed, and the QML will call the read function automatically.
It could be implemented like that Item{ myQmlProp: MyCppInst("myDBKey").value }.
If you know the db keys at compile time you could just add a property for each of them in your MyCppInst directly, or if you know them at the creation of your cpp class you could put them in a QQmlPropertyMap.
Usage would be like that : Item { myQmlProp: MyCppInst.myDbKey } (or MyCppInst["myDbKey"] if you need to be dynamic in the QML side).

Qt C++; How to address a certain textfield, depending on user input

I'm having issues with formulating the question but this is what I want to do with my application.
A user can select one or multiple image-files (.ppm), and they are displayed in some sort of legend, with their filename underneath. The information of these images is stored in a structure. (This structure contains the image path, name, and other info).
Now I want to give the user the chance to change the name of the selected images, and uses this name in the rest of the application. So I would have to change the name in the structure.
I could do this by adding textfields in the legend, where users can type the desired name, but how can I get the input from this textfield if I don't know which one is alterred?
If the user selects 6 images, I need 6 new textfields in the legend, but how can I address the correct one?
struct[2].name = input2.getText();
I also thought about doing it with some sort of wizard, with 6 pages where the names can be changed, but I don't know how I can adress the correct textfield.
Any help would be welcome, thanks!!
If you want to allow users to rename multiple files at one time, you may want to create a wizard. In the wizard you could display each picture they selected (one at a time) and allow them to rename each picture (one at a time). Otherwise it will be confusing to the user and harder for you to manage.
When generating the wizard, I would use the information structure to associate the picture with the textfield.
Qt, signals and slots are your friend here.
When you setup the textfields for the name, assuming you use something like a QLineEdit object, connect to a relevant signal, such as editingFinished(). Make the connection to the slot of the Object that stores the structure. The receiving slot then updates the appropriate information.
So, assuming your struct is in an object derived from QObject, you can do something like this: -
struct DataStruct // the struct storing the underlying data
{
QString name;
QLineEdit* linkedEditWidget; // widget for user to change text
};
class MainObject : public QObject
{
Q_OBJECT // required for signals and slots
public slots:
void UpdateText();
private:
const int NUM_STRUCTS = 10; // initialisation in C++ 11
DataStruct myStructs[NUM_STRUCTS]; // a number of structs
};
When you initialize the array of structs and the LineEdit widgets, store a pointer to the matching LineEdit widget in each myStruct and connect the widgets' editingFinished signals to the MainObject updateText() slot. It would be a good idea to use weak smart pointers here, but I'll use a standard pointer, to keep things simple.
When you receive notification that the text has changed, you'll need to match up the caller with the LineEdit* in the struct. Note that QObject::sender() will return the pointer to the object that sent the message: -
void MainObject::UpdateText()
{
QObject* theSendingWidget = sender();
for(int i=0; i<NUM_STRUCTS; ++i) // assuming NUM_STRUCTS is already defined
{
if(myStructs[i].linkedEditWidget == theSendingWidget)
{
// update the name in the data struct
myStructs[i].name = (static_cast<QLineEdit*>(theSendingWidget))->text();
return; // our work is done.
}
}
}
Finally, you'd probably make life easier for yourself by storing the data in Qt model objects, rather than using a plain struct. I suggest reading up on Model / View programming and the MVC design pattern

Qt QTreeView not updating when adding to 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

Sending objects as signal arguments between threads in Qt

I am new to Qt and trying to learn the Qt threading mechanism. I am in a situation where I would like a background thread to perform some long running task and report the results to another (or main) thread after processing every 100 items. Right now I am doing this by emitting a signal from the background thread containing a list of the processed objects that is received in a slot in the main thread. Does Qt make a copy of the signal argument when it is received in the slot ? If so, how does how does calling qRegisterMetaType help with that ? This is what I am tying to accomplish in my code :
//background thread
void run(){
//get a query object from database
int fireCount = 0;
QList< QList<QVariant> > data;
while(query->next()){
fireCount++;
QList<QVariant> row;
//do some calculations on the fields read from the query
processRow(query,&row);
data.append(row);
if(fireCount>100){
emit publishDataToMainThread(data);
fireCount = 0;
data.clear();
}
}
}
//slot in main thread
void receiveData(QList< QList<Qvariant> > data){
\\display the data
}
Also , is this a recommended practice for transferring objects between threads ?
This is a perfectly fine way of doing it. QList uses implicit sharing (i.e. copy on write) so copying it means copying one pointer and increasing the reference count. It only gets copied once you try to modify it.
Just remember to use Qt::QueuedConnection when connection the signal to the slot so that the slots gets run in the receivers thread.
qRegisterMetaType or Q_DECLARE_METATYPE are needed so that you can pass parameters by value in signals. It tells the Qt Metatype system (which is sort of like reflection) that this type exists.

Create a proxy of QAbstractModelItem

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.