I have the following JSON-file :
{
"users":[
{"nom":"123",
"name":"John",
"family":"ala",
"cash":1000
}
,{"nom":"456",
"name":"Joe",
"family":"ala",
"cash":1000
}
,{"nom":"131",
"name":"David",
"family":"ala",
"cash":1000
}]
}
I would like to change John's cash.
This is how I am trying to achieve this:
QFile f("file address ...");
f.open(QIODevice::ReadOnly|QIODevice::Text|QIODevice::WriteOnly);
QByteArray b=f.readAll();
QJsonDocument d=QJsonDocument::fromJson(b);
QJsonObject o=d.object();
for (int i=0;i<o["users"].toArray().size();i++) {
if(o["users"].toArray()[i].toObject()["name"].toString()=="John")
o["users"].toArray()[i].toObject()["cash"].toInt()=2000;//error unable to assign
}
However, I am getting the following error:
error: unable to assign
How to fix this?
Cause
You get the error, because you are trying to assign a value to the return value of a function (QJsonValue::toInt in this case).
Solution
Assign the value to QJsonValue, as demonstrated in the JSON Save Game Example:
void Character::write(QJsonObject &json) const
{
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
}
Example
Here is an example I have written for you, in order to demonstrate how your code could be changed to implement the proposed solution:
#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile file("data.json");
if (file.open(QFile::ReadOnly | QFile::Text)) {
QJsonObject json = QJsonDocument::fromJson(file.readAll()).object();
QJsonArray users = json["users"].toArray();
file.close();
for (auto user : users) {
QJsonObject userObj = user.toObject();
if (userObj["name"].toString() == "John")
userObj["cash"] = 2000;
user = userObj;
}
qDebug() << users;
}
return a.exec();
}
Result
The given example produces the following result:
QJsonArray([{"cash":2000,"family":"ala","name":"John","nom":"123"},{"cash":1000,"family":"ala","name":"Joe","nom":"456"},{"cash":1000,"family":"ala","name":"David","nom":"131"}])
Please note, that the cash for John is set to 2000.
Related
I need to get all possible currency symbols and currency abbreviations in my program in an array. How can I achieve this with C++ or possibly with Qt?
Parse Existing Up-To-Date Currencies List
I found the currency codes JSON list https://gist.github.com/Fluidbyte/2973986.
This list is regularly updated.
How the list could be parsed. Download the raw JSON text from the link. Save it to an UTF-8 encoded text file. Parse it somehow about written below.
#include <QtWidgets/QApplication>
#include <QVariantMap>
#include <QStringList>
#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QDebug>
#include <QPlainTextEdit>
using CurrencyMap = QMap<QString, QVariantMap>; // Use three-letter code as a key
using CurrencyMapIterator = QMapIterator<QString, QVariantMap>;
CurrencyMap parseCurrencyDataFromJson(QByteArray utf8Json)
{
CurrencyMap ret;
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(utf8Json, &parseError);
if (parseError.error != QJsonParseError::NoError)
{
qWarning() << parseError.errorString();
}
if (!document.isNull())
{
QJsonObject jobject = document.object();
// Iterate over all the currencies
for (QJsonValue val : jobject)
{
// Object of a given currency
QJsonObject obj = val.toObject();
// Three-letter code of the currency
QString name = obj.value("code").toString();
// All the data available for the given currency
QVariantMap fields = obj.toVariantMap();
ret.insert(name, fields);
}
}
return ret;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPlainTextEdit plainTextEdit;
plainTextEdit.show();
// File saved from the GitHub repo
QFile file("curr.json");
if (file.open(QIODevice::ReadOnly))
{
QByteArray jsonData = file.readAll();
CurrencyMap currencyMap = parseCurrencyDataFromJson(jsonData);
// Output all the available currency symbols to the plainTextEdit
CurrencyMapIterator currency(currencyMap);
while (currency.hasNext())
{
currency.next();
QString code = currency.key();
QVariantMap fileds = currency.value();
QStringList currencyInfo
{
code,
fileds.value("symbol").toString(),
fileds.value("symbol_native").toString()
};
plainTextEdit.appendPlainText(currencyInfo.join('\t'));
}
QString total = QString("\nTotal Available Currencies Count = %1")
.arg(currencyMap.count());
plainTextEdit.appendPlainText(total);
}
return a.exec();
}
QChar-Limited Solution
QChar and Unicode based solution. But be note that QChar itself supports codes up to 0xFFFF maximum only. Therefore only limited amount of symbols can be retrieved this way.
// In Qt, Unicode characters are 16-bit entities.
// QChar itself supports only codes with 0xFFFF maximum
QList<QChar> getAllUnicodeSymbolsForCategory(QChar::Category cat)
{
QList<QChar> ret;
// QChar actually stores 16-bit values
const static quint16 QCharMaximum = 0xFFFF;
for (quint16 val = 0; val < QCharMaximum; val++)
{
QChar ch(val);
if (ch.category() == cat)
{
ret.append(ch);
}
}
return ret;
}
Usage
QList<QChar> currencies = getAllUnicodeSymbolsForCategory(QChar::Symbol_Currency);
I need to parse a QML tree and get ids of all QML objects along the way which have it. I noticed that ids don't behave like normal properties (see the example below) – value returned from obj->property call is an invalid QVariant.
My question is – is there a way to retrieve object's id, even in some hacky (but reproductible) way?
Simplified example:
main.qml:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
Item {
id: howToGetThis
objectName: "item"
}
}
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QTimer::singleShot(1000, [&]() {
auto item = engine.rootObjects()[0]->findChild<QObject*>("item");
qDebug() << item->property("objectName");
qDebug() << item->property("id");
});
return app.exec();
}
Output:
QVariant(QString, "item")
QVariant(Invalid)
I think what you need is:
QString QQmlContext::nameForObject(QObject *object)
You can find the description here:
https://doc.qt.io/qt-5/qqmlcontext.html#nameForObject
Returns the name of object in this context, or an empty string if object is not named in the context. Objects are named by setContextProperty(), or by ids in the case of QML created contexts.
Based on comments received, a common pitfall is to call nameForObject using the wrong QQmlContext. (When that happens, you just the empty string.) To help with that, here is a more complete example:
QQuickItem* const focus_item = my_QQuickWindow->activeFocusItem();
if (!focus_item) {
fprintf(stderr, "no item has focus");
} else {
// There are many contexts in a hierarchy. You have to get the right one:
QQmlContext* const context = qmlContext(focus_item);
if (!context) {
// Unsure if this branch of code is even reachable:
fprintf(stderr, "item is not in any context?");
} else {
const QString focus_item_id = context->nameForObject(focus_item);
fprintf(stderr, "focus item: %s\n", focus_item_id.toStdString().c_str());
}
}
I'm trying to get the size of an executed query in SQLite but when I use last and try to use last its always false
Here is the code I'm trying to execute
void createDB() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","CREATE_CON");
db.setDatabaseName("C:/Desktop/TestDb.db3");
db.open();
QSqlQuery q(db);
q.exec("CREATE TABLE IF NOT EXISTS Test(testCol TEXT PRIMARY KEY);");
}
int entries() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","SELECT_CON");
db.setDatabaseName("C:/Desktop/TestDb.db3");
db.open();
QSqlQuery q(db);
q.exec("SELECT * FROM Test;");
if(q.last() == false) {
qDebug()<<q.lastError().text();
qDebug()<<db.lastError().text();
}
return q.at()+1;
}
The error text I get is empty so I don't know what I'm doing wrong.
I can create the database just fine so my database instance is working as it should.
Operating system: Windows 10
I'm using: Qt 5.10.1
Compilator: MinGW
The behavior is correct, if your table is empty there will not be a last element so last() will be false.
I will take this question to show you that you must validate all possible errors. A program can sometimes work but the duty of a good programmer is to prevent it from always working.
#include <QApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QPushButton>
static bool createDB() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","CREATE_CON");
db.setDatabaseName("TestDb.db3");
if(!db.open()){
qDebug()<<db.lastError().text();
return false;
}
QSqlQuery q(db);
if(!q.exec("CREATE TABLE IF NOT EXISTS Test(testCol TEXT PRIMARY KEY);")){
qDebug()<<q.lastError().text();
return false;
}
return true;
}
static int entries() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE","SELECT_CON");
db.setDatabaseName("TestDb.db3");
if(!db.open()){
qDebug()<< db.lastError().text();
return -1;
}
QSqlQuery q(db);
if(!q.exec("SELECT * FROM Test;")){
qDebug()<<q.lastError().text();
return -2;
}
if(!q.last()) {
return 0;
}
qDebug()<<"not empty";
return q.at()+1;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
if(!createDB()){
return -1;
}
QPushButton button("call entries");
QObject::connect(&button, &QPushButton::clicked, [&](){
qDebug()<<entries();
});
button.show();
return a.exec();
}
Instead of :
q.exec("SELECT * FROM Test;");
if(q.last() == false) {
qDebug()<<q.lastError().text();
qDebug()<<db.lastError().text();
}
You should do:
if(!q.exec("SELECT * FROM Test;")) {
qDebug()<<q.lastError().text();
}
And if you want to know the size of the query, why don't you store it in a QString, and call whenever you want QString::length()?
I'm using a QTableView to display some data inside a table. Because no vertical header is given Qt automatically assigns a row-id to a row. The example below displays the following table:
id|data
-------
1 | B
2 | A
3 | D
4 | C
After sorting the table based on the "data"-column:
id|data
-------
2 | A
1 | B
4 | C
3 | D
When a user double-clicks an entry, I want to be able to identify the clicked row by its id (i.e. A=2, B=1, C=4, D=3). Unfortunately, the methods used in "onDoubleClicked" only return the "new" row-id (i.e. A=1, B=2, C=3, D=4).
So how do I retrieve the correct row-id, when a user doubleclicks a row?
table.h
#ifndef TABLE_H
#define TABLE_H
#include <QTableView>
#include <QModelIndex>
#include <QHeaderView>
#include <QDebug>
class Table : public QTableView {
Q_OBJECT
public:
explicit Table() : QTableView() {
setSortingEnabled(true);
connect(this, &Table::doubleClicked, this, &Table::onDoubleClicked);
}
public slots:
void onDoubleClicked(QModelIndex index) {
qDebug() << index.row();
qDebug() << verticalHeader()->logicalIndex(index.row());
qDebug() << verticalHeader()->logicalIndexAt(index.row());
qDebug() << verticalHeader()->visualIndex(index.row());
qDebug() << verticalHeader()->visualIndexAt(index.row());
}
};
#endif // TABLE_H
main.cpp
#include <QApplication>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include "table.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Table table;
QStandardItemModel model;
QList<QStandardItem*> items;
items.append(new QStandardItem("B"));
items.append(new QStandardItem("A"));
items.append(new QStandardItem("D"));
items.append(new QStandardItem("C"));
model.appendColumn(items);
QSortFilterProxyModel proxy;
proxy.setSourceModel(&model);
proxy.sort(0, Qt::AscendingOrder);
table.setModel(&proxy);
table.show();
return a.exec();
}
The model is not having a new row(), you forgot that you are passing them via a proxy model. A proxy model holds different rows() and columns() from the original model because it can rearrange or filter fields.
The fix is easy - you just need to map the index from the proxy model to the original one.
void onDoubleClicked(QModelIndex index) {
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel*>(model());
auto sourceIdx = m->mapToSource(index);
qDebug() << sourceIdx.row();
qDebug() << verticalHeader()->logicalIndex(sourceIdx.row());
qDebug() << verticalHeader()->logicalIndexAt(sourceIdx.row());
qDebug() << verticalHeader()->visualIndex(sourceIdx.row());
qDebug() << verticalHeader()->visualIndexAt(sourceIdx.row());
}
These work for me.
void DeviceData::doubleClicked(QModelIndex cell)
{
cell.column();
cell.row();
}
Which you can then feed into your model: model.at(cell.row()).
Or you create a find() function.
if the value is bind with A you can use the setData method provided by QTableWidgetItem.set the value and get the value back by a specific flag.maybe Qt::UserRole
EDIT:
base on your code.
items.append(new QStandardItem("B"));
then you can get the QStandardItem make the item.setData(1,Qt::UserRole) while 1 is the value which is bind to "B".Later you can get the bind value from item.data(Qt::UserRole).Note: it would return a variant then you need to transfer it toInt
I'm trying to generate a simple table (2 rows and 2 columns) and write it to a pdf file, using Qt 4.8.0.
So far, I generate the pdf but there is extra space at the bottom of the "printed" table:
I got the same problem with the right side of the table but I managed to get rid of it. But in this case I am clueless.
Here's the code I have now (all of this code is located in main.cpp):
Main
#include <QtGui/QApplication>
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtGui/QPrinter>
#include <QtGui/QHeaderView>
#include <QtGui/QPainter>
#include <QtGui/QTableWidget>
#include <QtGui/QTableWidgetItem>
/**/
/* Here are the functions.
/**/
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMap<QString,int> values;
values.insert("X",7);
values.insert("Y",13);
bool status = TableWidgetToPdf("FromWidget.pdf",values);
return a.exec();
}
TableWidgetToPdf
bool TableWidgetToPdf(const QString& title, const QMap<QString, int>& values) {
QTableWidget* table = GenerateTable(values);
QPrinter printer;
printer.setOutputFileName(title);
printer.setOutputFormat(QPrinter::PdfFormat);
QPainter painter(&printer);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
printer.setPaperSize(QPrinter::A4);
table->render(&painter);
painter.end();
printer.newPage();
delete table;
return true;
};
GenerateTable
QTableWidget* GenerateTable(const QMap<QString,int>& values) {
QTableWidget* table = new QTableWidget;
table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setRowCount(2);
table->setColumnCount(2);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setShowGrid(false);
table->verticalHeader()->hide();
QStringList h_labels;
h_labels << "X" << "Y";
table->setHorizontalHeaderLabels(h_labels);
table->horizontalHeader()->setFont( QFont("Times", 10, QFont::Bold) );
table->horizontalHeader()->setStretchLastSection(true);
QTableWidgetItem* item00 = new QTableWidgetItem( QString::number(values["X"]));
item00->setTextAlignment(Qt::AlignCenter);
table->setItem(0,0, item00 );
QTableWidgetItem* item01 = new QTableWidgetItem( QString::number(values["Y"]) );
item01->setTextAlignment(Qt::AlignCenter);
table->setItem(0,1,item01);
table->setItem(1,0,new QTableWidgetItem("ABCD"));
return table;
};
NOTE:
Putting
table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
in GenerateTable the space disappears, but the cells are resized and consume too much space than needed for their contents. I would like to avoid that if possible:
EDIT:
OK.
In the end I achieved what I wanted by getting rid of the QTableWidget. I had to create the table using html and feeding it to a QTextEditor. Isn't any way to achieve this with a QTableWidget?
Have you tried the flags for resize content?
Try the following code, I don't have access to Qt right now.
table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
table->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
Hope that works!
I realise that this is an old post but it seems fairly often read.
I tried the same methods that you tried and none worked. Eventually I used a QTableView and added an extra method called by adding/removing rows.
void
TitleView::verticalResizeTableViewToContents()
{
auto count = m_model->rowCount(QModelIndex());
auto scrollBarHeight = horizontalScrollBar()->height();
auto horizontalHeaderHeight = horizontalHeader()->height();
auto rowTotalHeight = scrollBarHeight + (horizontalHeaderHeight * count);
setMinimumHeight(rowTotalHeight);
setMaximumHeight(rowTotalHeight);
}