Reading from .can files - qt

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

Related

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.

Changing and removing values from deeply nested QVariant

I'm using QVariant to manage the project settings of our In-House application.
For this I'm using a nested QVariantMap recursively containing QVariantMaps and leaves holding the actual values.
Now, I found it quite cumbersome to set and remove nodes from this tree like structure. Especially, if the nesting reaches a certain depth.
Part of the problem is, that value<T> returns a copy instead of a reference. (I'm wondering, why Qt is lacking this feature??)
#include <QVariantMap>
#include <QDebug>
int main(int argc, char** args) {
QVariantMap map = { {"A", QVariantMap{ {"B",5.} }} };
{
// Change value
auto nested = map["A"].value<QVariantMap>();
nested["B"] = 6;
map["A"] = nested;
qDebug() << map;
// What it should be
// map["A"]["B"] = 5;
}
{
// Remove value
auto nested = map["A"].value<QVariantMap>();
nested.remove("B");
map["A"] = nested;
qDebug() << map;
// What it should be
// map["A"].remove("B");
}
}
What might be the easiest way to directly set and remove values and to make my function a one-liner? Performance is not critical, but ease of usability is definitely an issue for me.
After some thought I came up to the idea to use a path to my desired value. This path should be unique.
The following code recursively finds the value and removes it. Changing a value should be quite similar. I'm not sure, if there might an easier approach.
bool remove(QVariantMap& map, const QString& path, QString sep = ".") {
auto elems = path.split(sep);
if (elems.size() > 1) {
if (!map.contains(elems.first())) return false;
auto tmp = elems;
tmp.pop_front();
auto childMap = map[elems.first()].value<QVariantMap>();
bool ret = remove(childMap, tmp.join("."));
if (!ret) return false;
map[elems.first()] = childMap;
return true;
}
else if (elems.size() == 1) {
return map.remove(elems[0]) >= 1;
}
else {
return false;
}
}
Remark
This solution should not be used, if there is a lot of data, as there is quite a lot of copying of maps.

Saving a qlistwidget after closing application

I have a program that allows for a user to create a profile that saves values using qsettings, the user accesses their profile by clicking on the name in a qlistwidget. I am trying to save the names of the profiles by using a text file but I am having trouble saving more than one profile name at a time. thank you! here is the code:
for saving a profilename to the text document
void Profile::writeProfilenames()
{
QString profilename = ui->lineEdit_profilename->text();
profilename = profilename.simplified();
QFile pfile("profilenames.txt");
if (!pfile.open(QFile::WriteOnly | QIODevice::Text))
{
return;
}
QTextStream out(&pfile);
out << profilename;
pfile.flush();
pfile.close();
}
for retrieving the profile names from the document
void Profile::readProfilenames()
{
QFile pfile("profilenames.txt");
if (!pfile.open(QIODevice::ReadOnly |
QIODevice::Text))
{
return;
}
QString proname = pfile.readLine();
QListWidgetItem *itm = new QListWidgetItem;
itm->setText(proname);
ui->listWidget_profiles->insertItem(0,itm);
}
P.S. if you know of a better way to do this then feel free to share! (with example please)
I don't quite see why you're saving the list of names in a text file, while the settings themselves are saved in a platform-specific fashion using QSettings.
The code you show has several problems:
Presumably you don't want to "write" the name to the file, overwriting the existing contents at the beginning, but specifically to append to the file. You also must specify a writable path to the file, so far you're using the current working directory that is: variable, not under your control, and not necessarily writable. Your code also doesn't handle repeated names.
QFile is a proper C++ class, and embodies the RAII principles. You don't have to do anything to flush and close the file. The compiler takes care of generating the proper code for you. That's why you're using C++ and not C, after all. Yes, your code compiles, but it reads like C, and such verbosity is unnecessary and counterproductive.
You're only retrieving one name from the file. You want to retrieve all of them.
I'd say that you should dispense with the file access, set up your application's identification, a crucial prerequisite to using QSettings, and, finally, use them:
struct Profile {
QString name;
int age;
}
void saveProfiles(const QList<Profile> & profiles)
{
QSettings s;
s.beginWriteArray("profiles");
for (int i = 0; i < profiles.size(); ++i) {
s.setArrayIndex(i);
const Profile & p = profiles.at(i);
s.setValue("name", p.name);
s.setValue("age", p.age);
}
s.endArray(); //optional
}
QList<Profile> loadProfiles()
{
QList<Profile> profiles;
QSettings s;
int size = s.beginReadArray("profiles");
for (int i = 0; i < size; ++i) {
s.setArrayIndex(i);
Profile p;
p.name = s.value("name").toString();
p.age = s.value("age").toInt();
profiles << p;
}
s.endArray(); // optional
return profiles;
}
int main(int argc, char ** argv) {
QApplication app(argc, argv);
app.setOrganizationName("fluxD613"); // ideally use setOrganizationDomain instead
app.setApplicationName("fluxer");
...
return app.exec();
}
After a lot more research and trial and error I came up with the following code that does the trick:
this function is implemented when I close the profiles dialog window and return to the main window using QCloseEvent.
void Profile::writeProfilenames()
{
QFile pfile("profilenames.txt");
if (!pfile.open(QFile::WriteOnly | QIODevice::Text))
{
return;
}
for(int row = 0; row < ui->listWidget_profiles->count(); row++)
{
QListWidgetItem *item = ui->listWidget_profiles->item(row);
QTextStream out(&pfile);
out << item->text().simplified() << "\n";
}
pfile.close();
}
reading the list of profilenames is implemented when I open the dialog window just under ui->setup(this).
void Profile::readProfilenames()
{
QFile pfile("profilenames.txt");
if (!pfile.open(QIODevice::ReadOnly |
QIODevice::Text))
{
return;
}
QTextStream in(&pfile);
while (!in.atEnd())
{
QString line = in.readLine();
QListWidgetItem *item = new QListWidgetItem;
item->setText(line);
ui->listWidget_profiles->addItem(item);
}
pfile.close();
}
I am now working on making sure the user does not enter a profilename that already exists and deleting a profilename from the QListWidget.

Config File Overwritten in Qt

I am creating a config file which stores username, password and role of certain user. I am using the following code.
void MainWindow::OnAssignButtonClicked()
{
QSettings settings("/root/configFile.ini", QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QString Role = comboBox.currentText();
QList<QString> listUsername;
QList<QString> listPassword;
QList<QString> listRole;
QVariantMap userPasswordMapping;
QVariantMap userRoleMapping;
listUsername << userName;
listPassWord << Password;
listRole << Role;
for(int i = 0; i < listUsername.size() ; i++)
{
QString user = listUsername.at(i);
QString pass = listPassword.at(i);
QString role = listRole.at(i);
userPasswordMapping[user] = pass;
userRoleMapping[user] = role;
}
// Store the mapping.
settings.setValue("Password", userPasswordMapping);
settings.setValue("Role",userRoleMapping);
}
While Reading the Values
QVariant variantPassword = settings.value("Password");
QVariant variantRole = settings.value("Password");
QVariantMap mapPassword = variantPassword.value<QVariantMap>();
QVariantMap mapRole = variantRole.value<QVariantMap>();
QMapIterator<QString, QVariant> iteratorPassword(mapPassword);
QMapIterator<QString, QVariant>iteratorRole(mapRole);
while (iteratorPassword.hasNext())
{
iteratorPassword.next();
QString user = iteratorPassword.key();
QString pass = iteratorPassword.value().toString();
iteratorRole.next();
QString role = iteratorRole.value().toString();
}
The first time the value gets written correctly. However if I run the program again the value over writes the old values. Can some one please suggest me a solution here?
Thank You
Every time you click your 'assign' button, you create new mappings, store the user, password and role in there and save that list to the config file.
What is missing is that you read the existing mappings before modifying them.
Instead of
QVariantMap userPasswordMapping;
QVariantMap userRoleMapping;
do:
QVariantMap userPasswordMapping = settings.value("Password").value<QVariantMap>();
QVariantMap userRoleMapping = settings.value("Role").value<QVariantMap>();
Additional hint about the code you posted about reading the values:
QVariant variantRole = settings.value("Password");
this should be
.value("Role");

How to properly use QSettings

I want to use QSettings to save highscores but it doesn't work properly. I'm saving and reading those values in 2 different files.
This is my code responsible for adding values into array:
QSettings settings;
settings.beginWriteArray("results");
int size = settings.beginReadArray("results");
settings.setArrayIndex(size);
settings.setValue("result", "qwerty");
and reading:
QSettings settings;
QString tmp = "";
int size = settings.beginReadArray("results");
for(int i = 0; i < size; ++i)
{
settings.setArrayIndex(i);
tmp += settings.value("result").toString();
}
ui->label->setText(tmp);
I would do it like this:
Lets say that we have two functions member of a class to load and save the scores.
To use the registry, you have to specify the application name and editor:
QSettings settings("<MyEditorName>","<myAppName>");
saveScores(settings);
loadScores(settings);
to use a file, you have to provide the file path and format:
QSettings settings("<filepath>",QSettings::iniFormat);
saveScores(settings);
loadScores(settings);
from your code and the documentation; the member function would be as follow.
The class countains a vector of scores (QVector mScores)
Function to save the scores:
void myClass::saveScores(QSettings& iSettings)
{
iSettings.beginGroup("Scores");
iSettings.beginWriteArray("results");
for(int i=0; i<mScores.count();i++)
{
iSettings.setArrayIndex(i);
iSettings.setValue("result",mScores[i]);
}
iSettings.endArray();
iSettings.endGroup();
}
Function to load the scores
void myClass::loadScores(QSettings& iSettings)
{
iSettings.beginGroup("Scores");
int size = iSettings.beginReadArray("results");
mScores.resize(size);
for(int i=0;i<size;i++)
{
iSettings.setArrayIndex(i);
mScores[i] = iSettings->value("results").toInt();
}
iSettings.endArray();
iSettings.endGroup();
}
I am using groups to provide better visibility in the saving file but you can remove them
The beginReadArray() after beginWriteArray() is causing the problem. Do this:
QSettings settings;
int size = settings.beginReadArray("results");
settings.endArray();
settings.beginWriteArray("results");
settings.setArrayIndex(size);
settings.setValue("result", "qwerty");
settings.endArray();
Note you need to call endArray() when finished.
Using QSettings to read an ini file also showing the required format of the expected ini file\n
Format of alphabet.ini :
[A_SECTION]
AA=20
BB=40
CC=0
[B_SECTION]
DD=100
EE=270
FF=3000
Simple code to read alphabet.ini :
QSettings settings("C:\\Qt\\qtcreator-2.5.2\\testingProg\\alphabet.ini",QSettings::IniFormat);
settings.beginGroup("A_SECTION");
const QStringList AchildKeys = settings.childKeys();
QHash<QString, QString> Avalues;
foreach (const QString &childKey, AchildKeys)
{
Avalues.insert(childKey, settings.value(childKey).toString());
qDebug() << childKey << " : " <<settings.value(childKey).toString();
}
settings.endGroup();
qDebug() << ";
settings.beginGroup("B_SECTION");
const QStringList BchildKeys = settings.childKeys();
QHash<QString, QString> Bvalues;
foreach (const QString &childKey, BchildKeys)
{
Bvalues.insert(childKey, settings.value(childKey).toString());
qDebug() << childKey << " : " <<settings.value(childKey).toString();
}
settings.endGroup();
The output:
"AA" : "20"
"BB" : "40"
"CC" : "0"
"DD" : "100"
"EE" : "270"
"FF" : "3000"

Resources