How to declare a negative number on a QTableView using SQlite - sqlite

I am designing a major user interface with several fields.
In order to shrink the problem I created a small minimal application with 5 columns: name, image, dataDatabase, dateTime and coordNumber.
I have a MainWindow with a QTableView, as soon as I right click inside the QTableView an AddItemDialog opens up with:
1) nameLineEdit
2) ImLineEdit
3) imageLineEdit
4) dateTimeEdit
5) numLineEdit
The issue that I have is that I can't find a way to accept the 5) numLineEdit through the AddItemDialog when the number is negative.
Currently it only saves positive numbers. How to handle this exception?
I read from official document but I could not figure out. However always from this official source it seems that a negative number must be interpreted as "no limit" value. The explanation was short and didn't provide any useful small example, so I am still not sure how to proceed.
I am including the most important parts of the application below with the related description of the procedure I followed:
I created an Item with the fields item.h:
class Item
{
public:
Item(const double dateTime,
const QString &name = "", const QString &image = "",
int num, const QByteArray &imagesData = QByteArray());
QString name() const { return mName; }
QString image() const { return mImage; }
QByteArray imagesData() const { return mImagesData; }
double dateTime() const { return mDateTime; }
int num() const { return mNumberCoord; }
private:
QString mName;
QString mImage;
QByteArray mImagesData;
double mDateTime;
int mNumberCoord
};
and its related item.cpp
Item::Item(const double dateTime, int num,
const QString &name, const QString &image, int num,
const QByteArray &imagesData) :
mName(name),
mImage(image),
mImagesData(imagesData),
mDateTime(dateTime),
mNumberCoord(num)
{
}
I created a database.h table that will contain the parameters as follows:
class dataBase : public QObject
{
Q_OBJECT
public:
explicit dataBase(QObject *parent = nullptr);
bool inizializationDataBase(const QString &nameDataBase);
bool configureDataBase();
QString getError() const { return mError; }
bool addItem(const Item &item);
private:
QSqlDatabase mDatabase;
QString mError;
};
And its related database.cpp file - I am only including the most important piece of the code for this file:
#define CREATE_TABLE \
" CREATE TABLE IF NOT EXISTS Fish_Table" \
" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
", name TEXT NOT NULL" \
", image TEXT NOT NULL" \
", dataDataBase BLOB NOT NULL" \
", dateTime DOUBLE NOT NULL" \
", num INTEGER NOT NULL)"
dataBase::dataBase(QObject *parent) : QObject(parent)
{
}
bool dataBase::addItem(const Item &item) {
QSqlQuery q;
q.prepare("INSERT INTO Fish_Table (name, image, dataDatabase, dateTime, num) VALUES (?,?,?,?,?)");
q.addBindValue(item.name());
q.addBindValue(item.image());
q.addBindValue(item.imagesData());
q.addBindValue(item.dateTime());
q.addBindValue(item.num());
bool ok = q.exec();
if (!ok) {
mError = q.lastError().text();
}
return ok;
}
and finally the AddItemDialog.cpp that contains the fields I am trying to pass to the QTableView of the MainWindow.
AddItemDialog.cpp
void AddItemDialog::on_buttonBox_accepted()
{
QFile dataBase(ui->imageLineEdit->text());
if (!dataBase.open(QIODevice::ReadOnly)) {
QMessageBox::critical(this, "Error", dataBase.errorString());
return;
}
mItem = Item(ui->dateTimeEdit->dateTime(),
ui->nameLineEdit->text(),
ui->ImLineEdit->text(),
ui->numLineEdit->text(),
dataBase.readAll());
dataBase.close();
accept();
}
I expect to save either positive or negative numbers on the QTableView, but as of now I can only save positive numbers. How to handle this exception?

So the answer is yes, it is possible to handle negative numbers. For some reasons I thought that SQL was not able to do that but below is a small example of how to handle that exception:
#define CREATE_TABLE \
" CREATE TABLE IF NOT EXISTS Fish_Table" \
" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
", name TEXT NOT NULL" \
", image TEXT NOT NULL" \
", dataDataBase BLOB NOT NULL" \
", dateTime DOUBLE NOT NULL" \
", num INTEGER NOT NULL)"
dataBase::dataBase(QObject *parent) : QObject(parent)
{
}
bool dataBase::addItem(const Item &item) {
QSqlQuery q;
q.prepare("INSERT INTO Fish_Table (name, image, dataDatabase, dateTime, num) VALUES (name,descriptionOfImage,BLOB(actual image), put date&time,-200)");
q.addBindValue(item.name());
q.addBindValue(item.image());
q.addBindValue(item.imagesData());
q.addBindValue(item.dateTime());
q.addBindValue(item.num());
bool ok = q.exec();
if (!ok) {
mError = q.lastError().text();
}
return ok;
}
This is also a very valuable source.
If also there is any need to turn a positive number into a negative number than this is a possible solution:
UPDATE Table
SET field= (field * -1)
Where SIGN(field) = -1
Also this is very useful in case someone needs the SQL code for an additional source.

Related

Is there a map-like tool in QT that can be iterated over inserted index?

From the Qt documentation about QMap::iterator :
Unlike QHash, which stores its items in an arbitrary order, QMap
stores its items ordered by key. Items that share the same key
(because they were inserted using QMap::insertMulti(), or due to a
unite()) will appear consecutively, from the most recently to the
least recently inserted value.
What I want is to interate a map by inserted index. For example this map.
const static QMap<QString, int> MEASUREMENT_COLUMNS{{"ID", MY_SQL_BIGINT}, {"logging_id", MY_SQL_INT}, {"calibration_id", MY_SQL_INT}, {"logging_comment", MY_SQL_VARCHAR255}, {"measurement_date_time", MY_SQL_DATETIME}, {"ADC0", MY_SQL_FLOAT},
{"ADC0", MY_SQL_FLOAT},
{"ADC1", MY_SQL_FLOAT},
{"ADC2", MY_SQL_FLOAT},
But the problem is as the documentation says above about QMap and QHashmap. They will not work for be if I want to iterate a map by inserted index.
For example, first ID, then logging_id, then calibration_id etc.
So I need to select something else than QMap and QHash.
Question:
Is there a map-like tool in QT that can be iterated over inserted index?
You can use two QVector, or use QVector<QPair<QString, int> > instead.
Here's the start of a QHash derivative which provides this functionality. DISCLAIMER: This is not entirely perfected! Not every function / feature of QHash has yet been accounted for. As long as you only use the functions / operator overloads provided here, you'll be fine for sure. If someone wants to keep developing this and repost a truly "finished" class, that would be great!
Note that performance will of course be degraded a bit, and memory consumption will increase, using this vs the natural QHash, but for small data sets that should be negligible.
OrderedHash.h
#ifndef ORDEREDHASH_H
#define ORDEREDHASH_H
#include <QHash>
#include <QVector>
#include <QDataStream>
#include <QDebug>
template<class K, class V>
class OrderedHash : public QHash<K,V>
{
public:
using QHash<K,V>::QHash;
#ifdef Q_COMPILER_INITIALIZER_LISTS
OrderedHash( std::initializer_list<std::pair<K, V>> list )
: QHash<K,V>::QHash()
{ foreach( auto p, list ) insert( std::get<0>(p), std::get<1>(p) ); }
#endif
// Returns the keys in the order they were inserted.
// If the ordered keys vector is blatantly out of sync with the hash
// (as may occur via the use of QHash functions not accounted for
// by this override!), this returns UNordered keys, since those are at
// least accurate.
QList<K> orderedKeys() const {
if( QHash<K,V>::size() != orderedKeys_.size() )
{
qWarning() << "OrderedHash keys are out of sync!";
return QHash<K,V>::keys();
}
return orderedKeys_.toList();
}
// This insert override "appends" to the "end" of the hash. If the key is
// already present, the entry is "moved" to the new end.
typename QHash<K,V>::iterator insert( const K &key, const V &value )
{
//qDebug() << "OrderedHash insert: " << key << ":" << value;
orderedKeys_.removeAll( key );
orderedKeys_.push_back( key );
return QHash<K,V>::insert( key, value );
}
// This additional update function perseveres the "key order" while
// modifying the value. If the key is not yet present, the entry is
// appended to the "end" of the hash.
typename QHash<K,V>::iterator update( const K &key, const V &value )
{
if( !QHash<K,V>::contains( key ) ) return insert( key, value );
return QHash<K,V>::insert( key, value );
}
int remove( const K &key )
{
orderedKeys_.removeAll( key );
return QHash<K,V>::remove( key );
}
void clear()
{
orderedKeys_.clear();
QHash<K,V>::clear();
}
private:
QVector<K> orderedKeys_;
};
// COPIED AND TWEAKED QT SOURCE FOR THESE STREAM OPERATOR OVERLOADS
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, OrderedHash<Key, T> &hash)
{
QDataStream::Status oldStatus = in.status();
in.resetStatus();
hash.clear();
quint32 n;
in >> n;
for (quint32 i = 0; i < n; ++i) {
if (in.status() != QDataStream::Ok)
break;
Key k;
T t;
in >> k >> t;
/* ORGINAL QT SOURCE
hash.insertMulti(k, t);
*/
//---------------------------------
hash.insert(k, t);
//---------------------------------
}
if (in.status() != QDataStream::Ok)
hash.clear();
if (oldStatus != QDataStream::Ok)
in.setStatus(oldStatus);
return in;
}
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const OrderedHash<Key, T>& hash)
{
out << quint32(hash.size());
/* ORGINAL QT SOURCE
typename QHash<Key, T>::ConstIterator it = hash.end();
typename QHash<Key, T>::ConstIterator begin = hash.begin();
while (it != begin) {
--it;
out << it.key() << it.value();
}
*/
//---------------------------------
const QList<Key> keys( hash.orderedKeys() );
foreach( auto key, keys ) out << key << hash.value(key);
//---------------------------------
return out;
}
#endif // ORDEREDHASH_H
Not in QT (to my knowledge, at least).
Can you use Boost, e.g. boost::multiindex? Another option is to combine map with vector in a class +- like this (this is likely to contain errors; it's supposed to illustrate the general idea, not to be a fully working piece of code):
template<typename K, typename V>
class indexed_map
{
map<K, V> m_map;
vector<K> m_insertionOrder;
public:
void insert(const K& k, const V& v)
{
m_map.insert(k,v);
m_insertionOrder.push_back(k);
}
V byKey(const K& k) const {return m_map.at(k)};
V byOrder(size_t n) const {return m_map.at(m_insertionOrder.at(n));}
};
Of course you'll have to write some boilerplate (ok, lots of it in fact), iterators might be also tricky.

Qt Example with HexStringValidator fails

I am using the following code from this example: https://doc.qt.io/qt-5/qtserialbus-can-example.html
enum {
MaxPayload = 8,
MaxPayloadFd = 64
};
HexStringValidator::HexStringValidator(QObject *parent) :
QValidator(parent),
m_maxLength(MaxPayload)
{
}
HexStringValidator::HexStringValidator(QObject *parent, uint maxLength) :
QValidator(parent),
m_maxLength(maxLength)
{
}
class HexStringValidator : public QValidator
{
Q_OBJECT
public:
explicit HexStringValidator(QObject *parent = nullptr);
explicit HexStringValidator(QObject *parent, uint maxLength);
QValidator::State validate(QString &input, int &pos) const;
void setMaxLength(int maxLength);
private:
uint m_maxLength = 0;
};
The problem is in this function:
I have a lineedit with this validator and call it with a valid Hex String.
ui->lineEditCANCommand->setValidator(new HexStringValidator(this, 4));
ui->lineEditCANCommand->setText("000003e9");
QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
const int maxSize = 2 * static_cast<int>(m_maxLength);
const QChar space = QLatin1Char(' ');
QString data = input;
data.remove(space);
if (data.isEmpty())
return Intermediate;
// limit maximum size and forbid trailing spaces
if ((data.size() > maxSize) || (data.size() == maxSize && input.endsWith(space)))
return Invalid;
// check if all input is valid
const QRegularExpression re(QStringLiteral("^[[:xdigit:]]*$"));
if (!re.match(data).hasMatch())
return Invalid;
// insert a space after every two hex nibbles
const QRegularExpression insertSpace(QStringLiteral("(?:[[:xdigit:]]{2} )*[[:xdigit:]]{3}"));
if (insertSpace.match(input).hasMatch()) {
input.insert(input.size() - 1, space);
pos = input.size();
}
return Acceptable;
}
The function is supposed to change this to "00 00 03 e9"
It however runs endless and creates
000003e 9
000003e 9
000003e 9
000003e 9
What is going wrong? Note, this code is from Qt and not mine.
In Qt6 problem should be gone.
There was related to this problem issue and two following commits:
Can example: Fix crash in payload hex editor,
CAN Example: Properly format payload hex string
It is possible to test since Qt 6.2.0.
First, I changed insertSpace regular expression to threeDigits:
QStringLiteral("[[:xdigit:]]{3}")
and insert space if 3 digits were added sequentially:
input.insert(match.capturedEnd() - 1, space);
That solves crash problem (infinite recursion).
Then I added auxilliary condition isEvenHex and formatter formatHexData with oneDigitAndSpace and threeDigits regular expressions that removes all extra spaces and add space after every two hex nibbles. I call them respectively on text change and sendButton click:
connect(m_ui->payloadEdit, &QLineEdit::textChanged, frameIdOrPayloadChanged);
connect(m_ui->sendButton, &QPushButton::clicked, [this]() {
...
m_ui->payloadEdit->setText(formatHexData(data));
...
}
Trying to solve your task I added:
QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
...
QString data = input;
const QRegularExpression twoSpaces(QStringLiteral("([\\s]{2})"));
if (twoSpaces.match(data).hasMatch())
return Invalid;
data.remove(space);
...
But there is one problem: if you are editing string in payload editor you may remove digits and two spaces can occure. The validator won't allow you remove characters between spaces. As a compromise I would introduce
threeSpaces(QStringLiteral("([\\s]{3})"));
validator instead of twoSpaces. With that one you may enter two spaces sequentially but still can edit string. I may ask QtSerialBus developers to include that change too.

Function validate of QAbstractSpinBox

I am reading this function in QT Docu
It says
QValidator::State QAbstractSpinBox::validate(QString &input, int &pos) const [virtual]
This virtual function is called by the QAbstractSpinBox to determine whether input is valid. The pos parameter indicates the position in the string. Reimplemented in the various subclasses.
I have a weird question because I dont really understand the document. The input here is a string, we determine whether the input is valid or not. So, why do we need the position in the string, for what? I thought the pos here is the length of the string but when I debugged, it is not true. So what does the pos here mean?
UPDATE:
Thanks to #mohabouje. In my case I use a class inherited from QAbstractSpinBox, I override this method and want to validate the string after changed it. How could I update this pos for validating?
QValidator::State MySpinBox::validate( QString &input, int &pos ) const
{
QString pureValue = stripped( input, &tmpPos, prefix(), suffix() ); //this is my function, i just want to remove also prefix and suffix
//I want to add group separator into the pureValue and validate it after that
//I want to add group separator here, not in the constructor with setGroupSeparatorShown(true);
//When i add group separator here, the separator appears simultaneously when we type
//When i set setGroupSeparatorShown(true) in the constructor, it appears after we finish editing and move to another thing (this element loses focus)
pureValue.insert(3, locale().groupSeparator());
input = pureValue;
// I think now 'pos' has changed, how could I update 'pos' to call the following function?
QValidator::State state = QDoubleSpinBox::validate( input, pos );
return state;
}
I was curious about the underlying implementation. I checked the source code in github.
QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
QValidator::State &state) const
{
if (cachedText == input && !input.isEmpty()) {
state = cachedState;
QSBDEBUG() << "cachedText was '" << cachedText << "' state was "
<< state << " and value was " << cachedValue;
return cachedValue;
}
const double max = maximum.toDouble();
const double min = minimum.toDouble();
QString copy = stripped(input, &pos);
QSBDEBUG() << "input" << input << "copy" << copy;
int len = copy.size();
...
}
The parameters are used in a private function called stripped. This is the source code:
QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
{
QString text = t;
if (specialValueText.size() == 0 || text != specialValueText) {
int from = 0;
int size = text.size();
bool changed = false;
if (prefix.size() && text.startsWith(prefix)) {
from += prefix.size();
size -= from;
changed = true;
}
if (suffix.size() && text.endsWith(suffix)) {
size -= suffix.size();
changed = true;
}
if (changed)
text = text.mid(from, size);
}
const int s = text.size();
text = text.trimmed();
if (pos)
(*pos) -= (s - text.size());
return text;
}
So, If I understand properly, given a string and prefix/suffix configuration, the function takes the string and computes the real size of the data to be validated, ignoring the prefix and suffix.
The function returns the data already validated that may be parsed to compute the numerical value.
The original value of pos, the function subtract the difference of the size of the text to be validated and the size of the text after performing the trimming operation.

QSqlTableModel inserting new record, getting data is QVariant(invalid)

I have a simple QSqlTableModel:
class UsersModel : public QSqlTableModel
{
Q_OBJECT
public:
UsersModel();
~UsersModel();
bool newUser(const QString &name, const QString &surname, const QString &birthday);
};
UsersModel::UsersModel()
{
setTable("users");
select();
}
When I'm inserting a new record into the model, it is inserted into the database, but immediate retrieving of this record from the model doesn't work:
bool UsersModel::newUser(const QString &name, const QString &surname, const QString &birthday)
{
QSqlRecord rec = record();
rec.setGenerated("id", true);
rec.setValue("name", QVariant(name));
rec.setValue("surname", QVariant(surname));
rec.setValue("birthday", QVariant(birthday));
bool res = insertRecord(-1, rec);
if (res) {
qDebug() << "last inserted id:" << query().lastInsertId();
qDebug() << "name: " << record(rowCount() - 1).value("name");
}
return res;
}
Result of newUser method call:
last inserted id: QVariant(qlonglong, 31)
name: QVariant(Invalid)
If I look into the database, record is inserted, but if I try to get it from the model, invalid QVariant is returned.
What I'm doing wrong? Shouldn't the model return data for the newly inserted record?

How to find the corresponding key value from the string inserted into a QMap?

I have the following data structure.
QMap<int,QString> map;
map.insert(0x01,"HELLO");
map.insert(0x02,"FELLOW");
map.insert(0x83,"NESTLE");
map.insert(0x044,"KITKAT");
QString str="NESTLE";
My requirement is to retrieve the value -0x83 when the string - NESTLE is passed.
Basically, based on str value, I want to get the corresponding key value i.e. 0x83 in this example. How can I do so?
I would do this:
[..]
QString str = "NESTLE";
int key = getKey(map, str); // should return 0x83 in your example.
[..]
int getKey(const QMap &map, const QString &value)
{
foreach (QString v, map) {
if (v == value)
return map.key(value);
}
return -1;
}
UPDATE:
Or the key could be simply found, by:
int key = map.key("NESTLE");
There are two approaches, depending on how many items are in the map.
If the map has few items (say <20), you can search for the key using map.key(). This performs a linear search over all items, and thus will perform badly for large maps due to O(N) complexity of such a search.
Alternatively, you can implement a bidirectional map. Shown below is a very trivial variant that only works for distinct T1 and T2. Note that there are no non-const index operators, since both maps would need to be modified. This would need a wrapper class.
template <typename T1, typename T2> class BiMap {
QMap<T1, T2> m_map1;
QMap<T2, T1> m_map2;
public:
typedef QMap<T1, T2>::iterator iterator1;
typedef QMap<T1, T2>::const_iterator const_iterator1;
typedef QMap<T2, T1>::iterator iterator2;
typedef QMap<T2, T1>::const_iterator const_iterator2;
iterator1 insert(const T1 & key, const T2 & value) {
m_map2.insert(value, key);
return m_map1.insert(key, value);
}
iterator2 insert(const T2 & key, const T1 & value) {
m_map1.insert(value, key);
return m_map2.insert(key, value);
}
const T1 & operator[](const T1 & key) const {
return m_map1[key];
}
const T2 & operator[](const T2 & key) const {
return m_map2[key];
}
};
BiMap<int,QString> map;
map.insert(0x01, "HELLO");
map.insert(0x02, "FELLOW");
map.insert(0x83, "NESTLE");
map.insert(0x044, "KITKAT");
int key = map["NESTLE"];
C++ algorithm should work fine:
#include <algorithm>
#include <iostream>
QMap<int,QString> map;
map.insert(0x01,"HELLO");
map.insert(0x02,"FELLOW");
map.insert(0x83,"NESTLE");
map.insert(0x044,"KITKAT");
QString str="NESTLE";
auto it = std::find_if(map.begin(), map.end(),
[&str](const QString &p){ return p == str; });
if(it != map.end())
std::cout << "0x" << std::hex << it.key();

Resources