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.
Related
I wrote the code below, but I get a notification saying No viable overloaded "=".
(Note that the list id contains some strings)
QList<QString>id;
QList<int>::iterator iter;
iter = std::find(logid.begin(), logid.end(), id);
The issue is that you are using the std::find function incorrectly. You are also trying to find inside a list another list.
Try this:
#include <QtDebug>
QList<int> logid = {1, 2, 3};
QList<QString> ids = {"2", "5"};
for (const auto &id : ids) {
auto it = std::find_if(logid.begin(), logid.end(), [&](const int x) {
return x == id.toInt();
});
if (it != logid.end()) {
// Valid item
qDebug() << "Address" << ⁢
qDebug() << "Value" << *it;
}
}
Note: since ids is a List of QString, you need to convert it to int.
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.
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.
I have a qTextEdit that I grab the text from (QString) and convert to a char* with this code:
QString msgQText = ui->textMsg->toPlainText();
size_t textSize = (size_t)msgQText.size();
if (textSize > 139) {
textSize = 139;
}
unsigned char * msgText = (unsigned char *)malloc(textSize);
memcpy(msgText, msgQText.toLocal8Bit().data(), textSize);
msgText[textSize] = '\0';
if (textSize > 0) {
Msg * newTextMsg = new Msg;
newTextMsg->type = 1; // text message type
newTextMsg->bitrate = 0;
newTextMsg->samplerate = 0;
newTextMsg->bufSize = (int)textSize;
newTextMsg->len = 0;
newTextMsg->buf = (char *)malloc(textSize);
memcpy((char *)newTextMsg->buf, (char *)msgText, textSize);
lPushToEnd(sendMsgList, newTextMsg, sizeof(Msg));
ui->sendRecList->addItem((char *)newTextMsg->buf);
ui->textMsg->clear();
}
I put the text into a qListBox, but it shows up like
However, the character array, if I print it out, does not have the extra characters.
I have tried checking the "compile using UTF-8" option, but it doesn't make a difference.
Also, I send the text using RS232, and the receiver side also displays the extra characters.
The receiver code is here:
m_serial->waitForReadyRead(200);
const QByteArray data = m_serial->readAll();
if (data.size() > 0) {
qDebug() << "New serial data: " << data;
QString str = QString(data);
if (str.contains("0x6F8C32E90A")) {
qDebug() << "TEST SUCCESSFUL!";
}
return data.data();
} else {
return NULL;
}
There is a difference between the size of a QString and the size of the QByteArray returned by toLocal8Bit(). A QString contains unicode text stored as UTF-16, while a QByteArray is "just" a char[].
A QByteArray is null-terminated, so you do not need to add it manually.
As #GM pointed out: msgText[textSize] = '\0'; is undefined behavior. You are writing to the textSize + 1 position of the msgText array.
This position may be owned by something else and may be overwritten, so you end up with a non null terminated string.
This should work:
QByteArray bytes = msgQText.toLocal8Bit();
size_t textSize = (size_t)bytes.size() + 1; // Add 1 for the final '\0'
unsigned char * msgText = (unsigned char *) malloc(textSize);
memcpy(msgText, bytes.constData(), textSize);
Additional tips:
Prefer using const functions on Qt types that are copy-on-write, e.g. use QBytearray::constData() instead of QByteArray::data(). The non-const functions can cause a deep-copy of the object.
Do not use malloc() and other C-style functions if possible. Here you could do:
unsigned char * msgText = new unsigned char[textSize]; and later delete[] msgText;.
Prefer using C++ casts (static_cast, reinterpret_cast, etc.) instead of C-style casts.
You are making 2 copies of the text (2 calls to memcpy), given your code only 1 seem to be enough.
I want to read .can files in Qt, I found out it is similiar to ini files so i used QSettings::IniFormat, i look for 2 attributes( say "rate" and "name").
code:
for(int i=0; i<files.count();i++)
{
QSettings file(files[i], QSettings::IniFormat);
QStringList keys = file.allKeys();
foreach(const QString& key, keys)
{
if(key.endsWith("/rate"))
{
QString Rate = file.value(key).toString();
qDebug() << Rate;
}
if(key.endsWith("/name"))
{
QString name = file.value(key).toString();
qDebug()<<name;
}
Problem is my can files has lot of "name" attribute, so this method is returning all the "name" attributes. I want to store the "name" attribute which the program finds right after "rate", there can be "name" attribute before "rate", so i just want to store the attribute which the program finds immediately after it finds "rate".
I don't know about .can files, I searched a little about them but couldn't find anything about them related to ini format.
Anyways, I rewrote your code to output the very first name attribute it finds after each rate encountered.
bool rateAttrFound = false;
for(int i=0; i<files.count();i++)
{
QSettings file(files[i], QSettings::IniFormat);
QStringList keys = file.allKeys();
foreach(const QString& key, keys)
{
if(key.endsWith("/rate"))
rateAttrFound = true;
if(key.endsWith("/name"))
{
if(rateAttrFound){
qDebug() << file.value(key).toString();
rateAttrFound = false;
}
}
}
}