I've been doing a code where a database is manipulated, elements are saved and edited by a Qsqlite database and query, so the way I've used is to pass query by parameter I don't know how bad it is but every time it goes By parameter I get this warning: " QSqlQuery is deprecated: is not mean to be copied: use move construction instead" and I wanted to know the correct way to do it, I show the MainWindow constructor, (the warning is indicated in the main).
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
char dirNow[1024];
db = QSqlDatabase::addDatabase("QSQLITE");
QString dirfull = QString(_getcwd(dirNow, 1024)) + "\\inventario.db";
db.setDatabaseName(dirfull);
if(!db.open()){
qDebug() << db.lastError().text();
}
model = new QSqlQueryModel();
QSqlQuery query(db);
if(!query.exec("CREATE TABLE IF NOT EXISTS articulo (codigo INTEGER NOT NULL, nombre VARCHAR(55) NOT NULL, unidades INTEGER NOT NULL, "
"categoria VARCHAR(55) NOT NULL, pais VARCHAR(55) NOT NULL, precio DOUBLE NOT NULL, foto VARCHAR(200) NOT NULL, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT) ")){
QMessageBox::information(this, "Error", query.lastError().text());
}
if(!query.exec("CREATE TABLE IF NOT EXISTS categorias(valor VARCHAR(55) NOT NULL) ")){
QMessageBox::information(this, "Error", query.lastError().text());
}
//query.prepare("DELETE FROM articulo WHERE = 1");
//query.addBindValue("");
"Warning is here: " id = imprimirArticulos(query);
"Warning is here: " QObject::connect(ui->registrarBtn, &QPushButton::clicked, this, [=]()->void{registrarArticulo(query); });
QObject::connect(ui->addImagenBtn, &QPushButton::clicked, this, [=]()->void{subirFoto();});
"Warning is here: " QObject::connect(ui->buscarBtn, &QPushButton::clicked, this, [=]()->void{filtroArticulos(query);});
"Warning is here: " imprimirCategorias(query);
"Warning is here: " QObject::connect(ui->categoriasCBox, &QComboBox::currentIndexChanged, this, [=]()->void{agregarCategorias(query);});
model->setQuery(std::move(query));
}
They want to make QSqlQuery object move only so only one query will exist.
id = imprimirArticulos(query) //creates copy of query
What you need to do is to move query:
id = imprimirArticulos(std::move(query))
If you try to use query again after moving it will be undefined behavior so you have to move it back from function to main to use it again in imprimirCategorias for example.
Expanding on fg's answer and edited: when you call a function passing by value your query object, the compiler makes a copy of it (as it does with any stack allocated object). The exact kind of copy depends, as usual, on the copy constructor of the class. For this class, it seems to be a shallow copy. Because of this, when the query object has associated resources (for instance, when you prepared the query and associated bind variables with it), all instances (the original one and the copy used by each function) will point to the same "internal" resources. And that may cause problems if (and only if) you change something in one instance of query, affecting the others.
It's not exactly the same case, but you can read it here: https://doc.qt.io/qt-6/qsqlquery-obsolete.html
QSqlQuery cannot be meaningfully copied. Prepared statements, bound
values and so on will not work correctly, depending on your database
driver (for instance, changing the copy will affect the original).
Treat QSqlQuery as a move-only type instead.
When using std::move(query), you obtain a "xvalue expression" of query, which can be directly passed to some library overloads. These overloads are actually move constructors (and, because they "may assume the argument is the only reference to the object", they will very probably "steal" the content of the object, leaving query in a "undefined behavior" state, not anymore usable by the calling function). See: https://en.cppreference.com/w/cpp/utility/move
The key point here is: if the query object is NOT modified (in your main code or in the called functions), you may: a) just dismiss the warning and keep the code as it is; or b) use std::move() and create a new query object for each call. You can't (you shouldn't) reuse the original query after calling std::move() on it. On the contrary, if you DO modify the query object (maybe inside one of the functions), your only option is creating separate instances.
Related
I have a QAction which I've assigned multiple shortcuts to it
test = new QAction();
this->addAction(test);
QList<QKeySequence> shortcuts;
shortcuts << QKeySequence(Qt::Key_N) << QKeySequence(Qt::Key_T);
test->setShortcuts(shortcuts);
connect(test,SIGNAL(triggered()),this,SLOT(SomeFucntion()))
In SomeFucntion I need to know which shortcut was pressed....Is there anyway of knowing that ?
You could try a more elaborate pattern with QSignalMapper that avoids the need to define as many actions as many shortcut you need, but requires c++11 (at least this implementation).
In the constructor of your window use the following code to declare your QShortcut objects and a QSignalMapper:
QSignalMapper* signalMapper = new QSignalMapper(this);
QShortcut* sc1 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_N), this);
QShortcut* sc2 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this);
connect(sc1, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
connect(sc2, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
signalMapper->setMapping(sc1, sc1);
signalMapper->setMapping(sc2, sc2);
QAction* action = new QAction();
connect(signalMapper, static_cast<void (QSignalMapper::*)(QObject*)>(&QSignalMapper::mapped),
[action](QObject *object){
QShortcut* sc = qobject_cast<QShortcut*>(object);
if (sc)
{
action->setData(sc->key().toString());
action->trigger();
}
});
connect(action, &QAction::triggered, this, &MainWindow::doStuff);
The 3rd connection is required because of the way QSignalMapper works: when a shortcut is activated, it will be notified to the QSignalMapper thanks to the 1st and 2nd connections, that will trigger the map() slot.
The QSignalMapper::map() slot will scan its mappings, made with the setMapping() API, whose first argument is the mapped object, and the second one is the parameter that will be used to emit the mapped() slot of the QSignalMapper, once the emitting object is identified. To do so, it uses the sender() method and simply compares the pointer returned to the mapped QObject pointers you provided as mappings.
Once the QObject is identified, QSignalMapper will emit the QSignalMapper::mapped(QObject*) signal, whose argument is the second argument given to setMapping, and in this case it the same as the first one, that is again a pointer to the QShortcut that was activated.
I used a lambda to catch this signal, and inside this lambda I simply check that the parameter given is a QShortcut pointer, and store its key sequence inside the data member of the QAction before triggering the action itself. The QAction::trigger() slot will then emit the QAction::triggered() signal that will in turn invoke your custom slot, in this case doStuff(). There you can retrieve the key sequence and do what you want with it.
So your slot implementation should look similar to this one:
void MainWindow::doStuff()
{
// use sender() to fetch data from action
QAction* act = qobject_cast<QAction*>(sender());
if (act)
{
QString sequence = act->data().toString();
// debug output will show you the triggering key sequence
qDebug() << sequence;
// use sequence string to determine which shortcut was used
// On Mike hint: better to reset data after use :)
act.setData(QVariant());
}
}
Note that I'm using a mapping based on QObject pointers. In this way you can reuse the signalMapper instance to connect events from other kind of QObjects (e.g. QPushButtons) and identify them in your custom slot as well setting a proper value for the QAction data member, that can store a generic QVariant istance.
Also when using QShortcut, be aware of their contex, that is when they are active, as it could be at widget or a window scope.
Unfortunately this pattern violates pure oop principles, but could be better than managing many actions (icon, text, tooltip etc...) for the same purpose.
EDIT: to answer comments
First of all, let me clarify that you can of course skip the use of QSignalMapper at all. This is just a possible solution (not the better, maybe an overkill... but not really worse in terms of performance).
A simpler way, as pointed by Mike in the comments consists in using lambdas for each QShotcut::activated signal, but this will result in copy/paste code, that I always try to avoid.
You can instead define a custom slot inside the MainWindow and use sender() to catch the QShortcut and prepare the action before triggering it.
Anyway, QSignalMapper IMHO, better explains what you are doing (from a semantic point of view) and is more flexible in case you need to expand the connection to other QObjects, supporting also other type of mappings.
Furthermore, but this is related to my personal taste, I like the idea to have fragments of code that are logically tied condensed into small snippets, instead of have it sparse among several slot/functions because it makes it easier to read and to trace back when I need to change it, of course only if this does not hurt the quality of code itself.
You should create a separate QAction for each shortcut and group them using a QSignalMapper.
I'm working on Qt 4.8.5. I'm using a QWizard structure with its QWizardPages (lets name them wp1, wp2, wp3,...). I need to pass one value from wp2 to wp4 but every time I try it, I get an empty string :(
The value I need is on a variable (QString sVar;) so not a widget and I've tried some things:
Using RegisterField with the wizardpage itselfs (as its still a type of qwidget) like this: registerField("myField",this); but ofcourse when i go to wp4 and try to qDebug()<< "data: " << field("myField").toString();it is empty.
I've see in some forums ppl saying that you can create a Q_PROPERTY and then use the register field. I've set it as Q_PROPERTY sData READ getData() WRITE setDATA() and then with registerField("myfield, this, ...and here I have a problem because i expect sData to apear but it doesn't.
So... any idea about how can I achieve this using registerField (I know I can also create my own slot and signal, emit it from wp2 and catch it up on wp4 but I would like to avoid it if possible)
Added the solution:
Class A.h:
class ClassA: public QWizardPage
{
Q_OBJECT
Q_PROPERTY(QString sAP READ getAP WRITE setAP)
....
public:
QString getAP() const {return AP;}
void setAP(QString s){AP=s;};
private:
QString AP;
Class A constructor:
registerField("AP_field",this, "sAP", SIGNAL(APChanged()));
Class A ::initializePage() function:
switch(m_iVar)
{
case 0 :...
break;
case 1:
setAP("AP1");
emit APChanged();
break;
}
And then in Class B (Where you need to know that data):
qDebug() << " AP QPROPERTY = " <<field ("AP_Field").toString();
According to the docs:
When we create a field using QWizardPage::registerField(), we pass a
unique field name and a widget. We can also provide a Qt property name
and a "changed" signal (a signal that is emitted when the property
changes) as third and fourth arguments; however, this is not necessary
for the most common Qt widgets, such as QLineEdit, QCheckBox, and
QComboBox, because QWizard knows which properties to look for.
So you still need a signal, but Qt will handle necessary connections for you and will catch your new value as you change it. You have to register like this:
registerField("myField", this, "myProperty", SIGNAL(myPropertyChanged()));
Then you have to remember to emit the signal each time you change your variable, and of course register it as a property.
This works from some but not all widgets that emit a signal. For QDoubleSpinWidgets,
QWizard::setDefaultProperty("QDoubleSpinBox", "value", SIGNAL(valueChanged(double)));
This is because the valueChanged() is for QString and double....does not know which value to take without the parameter specifying:
"Every time the value changes QDoubleSpinBox emits two valueChanged() signals, one taking providing a double and the other a QString. The QString overload provides the value with both prefix() and suffix(). The current value can be fetched with value() and set with setValue()." doc.qt.io/qt-5/qdoublespinbox.html I hope this never troubles others and this complete solution, no warnings, errors nor unpredictable behaviour.
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;
}
I am building a program with a largely sequential flow but also some alternative paths. I thought that a state machine might be the simplest way of implementing this since Qt provides such a class: QStateMachine (also see API).
However, I seem to have quite a number of states (20+). Also, I have a number of different transition events (let's say buttons 1-10). So e.g. pressing button x would cause a transition of state 13 to 14.
Entering or leaving each state should be able to execute specific functions with parameters, and while each state emits such signals, it is not possible to pass parameters, so that it requires a potentially large number of paramter-less functions.
Reimplementing QAbstractState also seems tedious for this matter, unless it would have methods similar to assignProperty() which allows setting QObject properties on "state-entry".
Is QSignalMapper along with several Signal-Slot-Connections for each state's transition signals to handle multiple actions an appropriate approach?
If you're using C++11, you can connect directly to a lambda that then invokes your function with a specified parameter.
Otherwise, figure out what object is the sender() of your signal, and set a dynamic property on that object. That property can be queried in the slot, and passed on to the function as a parameter.
For example (within a class):
void setup() {
QState *s2 = new QState();
s2->assignProperty(myLabel, "text", "state two");
s2->setProperty("prop", 0);
connect(s2, SIGNAL(entered()), io, SLOT(testSlot()));
}
Q_SLOT void testSlot() {
QObject *obj = this->sender();
QVariant prop = obj->property("prop");
qDebug() << __FUNCTION__ << prop.toString();
}
How would I go about passing QSqlQueryModel from a class that connects and queries the database through the control class or QMainWindow in my attempt and back to the widget needing the information?
I thought I could pass the reference location to the QSqlQueryModel object, but this is not working or I am doing something wrong.
I haven't found any examples showing what I am doing on the Qt Developer page.
Looks like these are just compiler errors, nothing specifically to do with Qt.
In short you are getting your pointers and references mixed up.
Error #1:
cardList = new List(sqlModel->getListModel());
You are passing a reference when the List takes a pointer. Fix your return type from getListModel or fix the above line.
Next, you are not specifying the second argument, i.e. the parent QWidget. Either specify your MainWindow as the parent, pass 0, or fix your constructor's signature to provide a default (generally 0).
Error #2:
List::List(QSqlQueryModel *model, QWidget *parent) : ListUI(parent){
setListItems(&model);
}
You receive the model as a pointer and then attempted to take the address of the pointer. I.e. You're making a double pointer. Change the line to
setListItems(model);
Hope that helps.