I have a understanding-problem why following code does not store any QString-Objects into my QList
QMap<QString, QList<QString> >map;
map = QMap<QString, QList<QString> >();
map.insert("eins", QList<QString>());
QList<QString> listInMap = map["eins"];
listInMap.append("test1");
listInMap.append("test2");
QList<QString> copyOfListInMap = map.value("eins");
qDebug() << copyOfListInMap.count();
Output: 0
The reason is simple: copy on write, aka. implicit sharing
QList<QString> listInMap = map["eins"];
At this point, you have not yet got a hard copy, only a "reference". That is not fair in the standard C++ sense, but imagine it as a "shallow copy". However, when you start appending in here, the list will copied and the original will left empty. It is because QList is implemented the way that is CoW (copy-on-write).
listInMap.append("test1");
listInMap.append("test2");
On a side note, you may wish to take a look at QStringList. While it inherits QList, it also has some additional convenience methods.
Now, you may be asking: How am I supposed to fill my map in, then?.
Multi map
I would personally suggest to use QMultiMap or at least QMap with insertMulti.
main.cpp
#include <QMap>
#include <QDebug>
#include <QString>
int main()
{
QMultiMap<QString, QString> map;
map.insert("eins", "test1");
map.insert("eins", "test2");
qDebug() << map.count();
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
2
Single map
If you stick to the current approach, I would suggest to either append the value into a new QStringList with which you will overwrite the value, or hold a reference to the exact same list.
Having said that, in your case, it looks a bit overkill to even consider an external storage for insertion. You ought to do that right away in my humble opinion.
main.cpp
#include <QMap>
#include <QDebug>
#include <QString>
int main()
{
QMap<QString, QStringList> map;
map.insert("eins", QStringList{"test1", "test2"});
qDebug() << map.value("eins").count();
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
2
According to the documentation your sample should be changed as follow:
QMap<QString, QList<QString> >map;
QList<QString> & listInMap = map["eins"]; // use reference here
listInMap.append("test1");
listInMap.append("test2");
QList<QString> copyOfListInMap = map.value("eins");
qDebug() << copyOfListInMap.count();
Related
I'm not sure what the correct way to create nested JSON from a loop is in Qt. Here's an example of what I mean
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSet>
#include <QString>
#include <QVector>
int main(int argc, char *argv[]) {
QVector<QSet<int>> sets;
QSet<int> testSet;
QJsonDocument doc;
QJsonArray root;
doc.setArray(root);
testSet.insert(0);
testSet.insert(1);
testSet.insert(2);
QSet<int> testSet1;
testSet1.insert(3);
testSet1.insert(4);
sets.push_back(testSet);
sets.push_back(testSet1);
for (auto set : sets) {
QJsonArray setJSON;
for (int i : set) {
setJSON.append(i);
}
root.append(setJSON);
}
qDebug(doc.toJson()); // [ ] -- empty?
}
In the code above, I have a list of sets of integers. I want to represent it as a nested array in JSON. This doesn't work though, because the QJson methods are all pass by reference, meaning that when the scope of the code changes, for instance leaving one of the for loops, the data is destroyed. Is the only way then to do this to create the nested objects on the heap using new? That seems strange to me.
Until now, Qt JSON works only with instance copy, as you see that QJson object is passed as const reference (&) in every set function.
The first QJsonDocument::setArray() take a copy of root instance as its main object. Change of root content after does not affect doc content.
You can verify the root content after the for block :
qDebug() << QJsonDocument( root ).toJson();
The doc variable should be assigned at where you want to take the actual root content:
doc = QJsonDocument (root);
Or:
doc.setArray(root)
Example code:
MainWindow::MainWindow(QWidget *parent) {
QString strTemp = typeid(this).name();
qDebug() << strTemp;
}
I want to get just the class name, in the above example I'm after just "MainWindow", what I get is:
class MainWindow *
Of course I can write a routine that takes this string and then strips out the class and pointer parts isolating just the class name. Is there anything that does this already?
In the absence of any standard routine, this is what I have done:
QString strTemp = typeid(this).name();
qDebug() << strTemp;
QStringList lstParts = strTemp.split(" ");
if ( lstParts.length > 1 ) {
strTemp = lstParts[1];
}
qDebug() << strTemp;
The above results in only the type name. In the end I just needed
__FUNCTION__ which gives the classname and method its being called from which will give:
MainWindow::MainWindow
cppreference says the following about std::type_info::name():
Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.
So the fact that you got class MainWindow as output here under one compiler does not mean that you will get the same output under different compiler (or even the same one but newer version).
Why not use boost::typeindex::type_id_with_cvr plus using some type traits, for example remove_pointer_t to get desired result ?
#include <iostream>
#include <sstream>
#include <string>
#include <boost/type_index.hpp>
class MainWindow {
public:
void test()
{
std::ostringstream os;
os <<
boost::typeindex::type_id_with_cvr<std::remove_pointer_t<decltype(this)>>().pretty_name()
;
std::string s = os.str(); // easy transform to QString
std::cout << s << std::endl;
}
};
int main()
{
MainWindow{}.test(); // as output MainWindow
return 0;
}
Full demo
You really should not rely on std::typeid, nor on boost or any other C++-only-library. None of them guarantee you will get the typename, especially with mangled types, on different compilers. Using virtual function even RTTI can influence the name of the type. Nothing prevents your compiler from renaming "MainWindow" to "CellarDoor". The only way to go so far is by using some sort of Reflection (which is not (yet) part of C++) or some preprocessor. ComicSansMS provided a nice answer, where you roll your own simple reflection implementation.
However, since you are using QT you can use QT Metaobjects for this. It actually has a method dedicated for this exact purpose; className.
QMetaObject::className() returns the class name as a string at run-time, without requiring native run-time type information (RTTI) support through the C++ compiler.
Simply call metaObject()->className():
MainWindow::MainWindow(QWidget *parent) {
qDebug() << metaObject()->className();
}
moc runs before any compiler and bakes the class name into the code. Looking into the moc generated code you will see something like:
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {
{
QT_MOC_LITERAL(0, 0, 10), // "MainWindow"
QT_MOC_LITERAL(1, 11, 13), // "buttonClicked"
QT_MOC_LITERAL(2, 25, 0) // ""
},
"MainWindow\0buttonClicked\0"
};
std::type_info::name() is a bad match for this kind of task. As pointed out by #pkrysiak's answer the return value of this function is implementation-defined, so it is not possible to strip it down to the type name as it appears in the source code in a portable way.
A better approach in this case would be to roll your own reflection mechanism, which will give you full control over the type string. Unfortunately, the language does not offer a lot of support for this at the moment, so the resulting mechanism will be a bit of a hack. For example, you could do something like this:
#define ENABLE_REFLECTION_TYPE_NAME(class_name) \
inline constexpr char const* type_name(class_name const&) { \
return #class_name; \
}
class Foo {};
ENABLE_REFLECTION_TYPE_NAME(Foo)
ENABLE_REFLECTION_TYPE_NAME(MainWindow)
MainWindow::MainWindow(QWidget *parent) {
QString strTemp = type_name(*this);
qDebug() << strTemp; // prints "MainWindow"
Foo f;
QString strTemp2 = type_name(f);
qDebug() << strTemp2; // prints "Foo"
}
cpp file:
#include "currency.hpp"
currencyNames[4] = {"gbp","usd","eur","aud"};
QComboBox *box1 = new QComboBox();
int i;
for(i=0; i < 4; i++){
QString *s = QString::string(currencyNames[i]);
box1->addItem(s);
}
hpp file:
#pragma once
#include string
.
.
.
static const int SIZE = 4;
std::string currencyNames[SIZE];
I keep getting a number of errors, I want an array containing the information above then loop through the array adding it to the QComboBox. Have no success. All relevant Qt headers included.
Besides the issues already stated in previous comments QComboBox::addItem method takes a reference to QString not a pointer.
Since you decided to use Qt framework you can embrace its collections which allows better interoperability with various widgets. Hence, your example could be rewritten a bit simplier.
For example:
QStringList currencyNames{"gbp","usd","eur","aud"};
QComboBox *box = new QComboBox();
box->addItems(currencyNames);
Just remember to assign the box to some parent to make it handle the box destruction when appropriate.
What is the best way to store multiple QFile in a collection? Would be best to use QList, array, or vectors?
I want to access the element of the collection as a QFile and not QFile*.
I'm trying to read a directory in Qt and store all the files in a collection. Then print all the file names to the screen.
How I'm reading the directory:
QDirIterator it(p_dir, QStringList() << "*.wav", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
{
// Put file into collection
}
Currently I'm using QList to store the QFiles:
QList<QFile*> list;
I read that the best way to get file information is to use QFileInfo type. How could I set the QFileInfo file to be the element from my list?
I want to do something like:
QFileInfo info;
QListIterator<QFile*> i(list);
while(i.hasNext())
{
info.setFile(i);
}
But the above gives me:
error: no matching function for call to 'QFileInfo::setFile(QListIterator&)'
fi.setFile(i);
Keep in mind QFile inherits QObject, and as such is not copyable or movable. It is common practice to store pointers to such objects in containers rather than the objects themselves. As for which container to use, it doesn't really matter much, but QVector will be the most efficient.
QListIterator is a Java style iterator. So:
QListIterator::QListIterator(const QList<T> & list)
Constructs an iterator for traversing list. The iterator is set to be
at the front of the list (before the first item).
The iterator doesn't point to anything, if you want to get the actual object, use i.next() to give you a QFile* which you then will have to dereference, since setFile() takes in a QFile &, not a QFile *.
QFile is not copyable. In C++98, there's generally no way of storing it in a container, unless the container supports in-place construction. I don't know offhand of any such C++98 containers, although writing one wouldn't be all that hard.
In C++11, you can use any container that doesn't need to copy elements and supports emplacement, e.g. std::list:
// https://github.com/KubaO/stackoverflown/tree/master/questions/qfile-list-36391586
#include <QtCore>
#include <list>
void populate(std::list<QFile> & files) {
QDir dir(QCoreApplication::applicationDirPath());
for (auto fileName : dir.entryList(QDir::Files)) {
qDebug() << "adding file" << fileName;
files.emplace_back(fileName);
}
}
void iterate(std::list<QFile> & files) {
for (auto & file : files)
if (file.open(QIODevice::ReadOnly)) {
qDebug() << "successfully opened" << file.fileName();
}
}
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
std::list<QFile> files;
populate(files);
iterate(files);
}
On my system, when run from Qt Creator, the output is:
adding file "main.o"
adding file "Makefile"
adding file "qfile-list-36391586"
successfully opened "main.o"
successfully opened "Makefile"
successfully opened "qfile-list-36391586"
Since QFile is a proper class implementing RAII of system resources, the files will get automatically closed by files::~list, before main returns.
I'm currently manipulating QFileInfo based on certain criterian/logics..but if I just filter all the complicated logic, it would be something like
QDir testDir("/home/mypath/to/dir");
QFileInfoList files = testDir.entryInfoList(); // #1. here you may use filters as I'm filtering based on a specific file extension
QFileInfoList myfiles;
for(int index = 0; index < files.size(); ++index)
{
const QFileInfo& info = files.at(index);
qDebug() << info.absoluteFilePath() << endl;
// #2. I've some sort of filters I used in #1 above that I compares here by utilizing info here, something like
//if (info.baseName().contains("anyPattern");
myfiles.push_back(info);
}
// Now you can view whatever you stored
for (int vw_idx = 0; vw_idx < myfiles.size(); ++vw_idx)
{
const QFileInfo& info = files.at(index);
qDebug() << info.absoluteFilePath() << endl;
}
In the following code the method exec(const char *cmd) runs a bash script and returns the output as a vector of strings. The intent is to create a QListWidgetItem out of each of those strings and add them to a QListWidget on a little GUI that I have created, but the QListWidgetItems are not created successfully. I always seem to be required to use a const char* to create either a QString or QListWidgetItem, it will not allow me to create one using a string variable.
You can see what I am going for in the line: QString nextLine = txtVcr.back();
There is an exception thrown here, it wants QString set to a const char*, for example QString nextLine = "Hello, World!";
How do I go about getting the strings from my vector and creating QListWidgetItems out of them to add to my QListWidget?
In C# everything was rather direct in that I could add strings or whatever else to any container/widget. Is there an intermediate step that I am overlooking with these "QWidgets"? Perhaps I should be casting to "Q" types?
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
vector<string> exec(const char *cmd);
vector<string> txtVcr = exec("/home/rhurac/getServices.sh");
while (!txtVcr.empty())
{
QString nextLine = txtVcr.back();
ui->uxListWidget->addItem(new QListWidgetItem(nextLine, ui->uxListWidget));
txtVcr.pop_back();
}
}
To get a QStringList from a std::vector<std::string>, you'll need to use QString::fromStdString on all the elements. For example:
#include <QStringList>
#include <algorithm>
#include <string>
#include <vector>
QStringList convert(const std::vector<std::string>& v)
{
using std::begin;
using std::end;
QStringList l;
l.reserve(v.size());
std::transform(begin(v), end(v), std::back_inserter(l),
&QString::fromStdString);
return l;
}
Then you can populate a QStringListModel feeding a QListView, as suggested in other answer.
Simply don't use QListWidgets and other QxyzWidget classes. They are depricated, and left in Qt for compatibility with old code (Qt3 basically).
Use QListView and QStringListModel for your use-case. E.g.
QListView *lv = new QListView();
QStringListModel m;
QStringList data = QStringList()<<"AAA"<<"BBB"<<"CCC";
m.setStringList(data);
lv->setModel(&m);
lv->show();
P.S.: Sorry, it doesn't answer your question directly. But unless you have to support legacy code, don't touch QListWidgets!