How to cast QList<QObject *> to QList<ADerivedQObjectClass*> - c++

I can't find a way to cast a QList of QObjects to another QList with another class template. I have a function that creates instances of QObjects and then return a QList. So my question is, can I cast that QList to another QList of different template class pointers ?
My code:
QList<QObject *> DbManager::listVO(QString voname)
{
DbManager::VOPropertiesJoin(DbManager::VOKeys(obj),VOJOIN_BOTH,"=");
QList<QObject *> vos;
QString voquery = "select * from %1";
voquery = voquery.arg(voname);
QSqlQueryModel *volist = DbManager::makeSelect(voquery);
for(int i = 0;i < volist->rowCount();i++){
QSqlRecord record =volist->record(i);
QObject *voinstance = DbManager::instantiateVO(voname,record.value(0),record.value(1),record.value(2),record.value(3),record.value(4),record.value(5),record.value(6),record.value(7));
vos << voinstance;
}
return vos;
}
and I want something like this:
QList<AnyClass*> list = DbManager::listVO("AnyClass");
Thanks for help in advance.

Since what you need at the end is a QList<AnyClass*>, you can try something like this (Assuming AnyClass is derived from QObject)
Note: I did some modifications to your original code to pass your list by reference since returning a huge list might not that be efficient. I didn't compile the code, but hoping you can figure out what is happening inside this snippet
void DbManager::listVO(QString voname, QList<AnyClass*>& listAnyClass)
{
DbManager::VOPropertiesJoin(DbManager::VOKeys(obj),VOJOIN_BOTH,"=");
// Initialise list with an empty list
listAnyClass = QList<AnyClass*>();
QString voquery = "select * from %1";
voquery = voquery.arg(voname);
QSqlQueryModel *volist = DbManager::makeSelect(voquery);
for(int i = 0; i < volist->rowCount(); i++)
{
QObject *voinstance = GetInstance(voname, volist->record(i));
// Trying to cast into an AnyClass
AnyClass* pAnyClass = qobject_cast<AnyClass*>(voinstance);
// If pAnyClass is a valid AnyClass* update your list
if(pAnyClass)
{
listAnyClass.append(pAnyClass);
}
}
}
QObject* DbManager::GetInstance(QString sVoname, const QSqlRecord& record)
{
return DbManager::instantiateVO
(
voname,
record.value(0),
record.value(1),
record.value(2),
record.value(3),
record.value(4),
record.value(5),
record.value(6),
record.value(7)
)
}
And after calling DbManager::listVO(QString voname, QList<AnyClass*>& listAnyClass) function, listAnyClass will get populated accordingly. This is efficient with contrast to returning a list.
Edited:
So in a nutshell you want to convert a QList<QObject*> into QList<AnyClass*>. So keep your original code as it is and implement a method like this where you wanna
do this conversion:
void ConvertToAnyClassList(const QList<QObject*>& listQObjects, QList<AnyClass*>& listAnyClass)
{
listAnyClass = QList<AnyClass*>();
for( int i = 0; i < listQObjects.length(); ++i )
{
AnyClass* pAnyClass = qobject_cast<AnyClass*>(listQObjects.at(i));
if(pAnyClass)
{
listAnyClass.append(pAnyClass)
}
}
}
And you can call it like this:
QList<QObject*> listQObjects = DbManager::listVO("AnyClass");
QList<AnyClass*> listAnyClass;
ConvertToAnyClassList(listQObjects,listAnyClass);
Suppose you have multiple types, so you may want to implement functions for each type like this:
ConvertToUserList(listQObjects,listUserObjects);
ConvertToCountryList(listQObjects,listCountryObjects);

Related

Create a new object from pointer reference C++

So, I've this code below:
foreach (QLineSeries* series, lineSeriesMap.values())
{
// ...
}
And I will modify series objects in this loop and I don't want to modify the original one, but create a new edited one. I'm extremely new to C++ and Qt so, I want something as the Java code below:
QLineSeries editedSeries = new QLineSeries(series);
I'm deleting elements, editing and re-ordering them from series by the way. But, as I said I need them both.
EDIT:
I've tried your answers but best way I believe is putting the code. This is a project made by some co-worker who changed jobs so its not my code, as i said I dont know C++.
chartwidget.h
void fillAreaSeries();
//...
QHash<QString,QLineSeries*> lineSeriesEntersMap;
QHash<QString,QLineSeries*> lineSeriesExitsMap;
chartwidget.cpp
void ChartWidget::fillAreaSeries() {
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
if (lineSeriesExitsMap.contains(seriesEnter->name())) {
QLineSeries* seriesExit = lineSeriesExitsMap.value(seriesEnter->name());
if (!((seriesEnter->points().size() == 1) && (seriesExit->points().size() == 1))) {
for(int i = seriesEnter->points().size() - 1; i > 0; i--)
{
if (seriesEnter->points().at(i - 1).y() > seriesEnter->points().at(i).y())
{
seriesEnter->removePoints(i, 1);
}
}
for (int i = seriesExit->points().size() - 1; i > 0; i--)
{
if (seriesExit->points().at(i - 1).y() < seriesExit->points().at(i).y())
{
seriesExit->removePoints(i-1, 1);
}
}
QVector<QPointF> editPoints = seriesExit->pointsVector();
std::sort(editPoints.begin(),editPoints.end(), [] (const QPointF & p1, const QPointF & p2)
{
return p1.y() < p2.y();
});
seriesExit->replace(editPoints);
qDebug() << "__Swap:__";
qDebug() << seriesEnter->points().at(0).y();
qDebug() << seriesExit->points().at(0).y();
qDebug() << seriesEnter->points().at(1).y();
qDebug() << seriesExit->points().at(1).y();
QAreaSeries* series = new QAreaSeries(seriesEnter, seriesExit);
series->setName(seriesEnter->name());
series->setOpacity(0.50);
series->setPen(Qt::NoPen);
series->setPointLabelsFormat(seriesEnter->name().split("-").at(0));
areaSeriesMap.insert(series->name(), series);
}
}
}
}
Edit 3:
So, QLineSeries contains QPointF list. I've the code below:
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
QLineSeries* entersToBeEdited = new QLineSeries(chart);
entersToBeEdited->setName(seriesEnter->name());
entersToBeEdited->points().append(seriesEnter->points());
//...
append doesnt work and returns 0 points. But I can set a name. I also tried appending by looping through items and adding it by
entersToBeEdited->points().push_back(seriesEnter->points().at(i));
and still nothing. I also tried << and += but no luck.
Looking at the class definition of QLineSeries, I don't see any simple way to copy your instance in order to duplicate it.
Thus you will have first to create a new instance :
QLineSeries editedSeries;
and manually copy the content of your original series in it.
editedSeries.append(originalSeries.points());
As you cannot modify the data once it is in the QLineSeries object, I would recommend to subclass QLineSeries or modify the QList<QPointF> obtained via originalSeries.points() before adding it to your new chart.
QLineSeries is not copyable, so you can't do what you want by modifying a copy. You will need to create a new QLineSeries from scratch.

QComboBox Array Access

I'd like to be able to access the values in my QComboBox without having to loop over the contents using itemText.
for( auto i = 0u; i < myQComboBox->count(); i++ )
{
result[i] = myQComboBox->itemText( i );
}
Is there a way that I can get to QComboBox's underlying QList so I can just use the operator[] or even better, iterators and range based loops?
It appears that you're hung on the syntax: you want to replace myQComboBox->itemText(i) with myQComboBox[i]. That can be rather easily done:
// implementation
class ModelAdapter {
QPointer<QAbstractItemModel> m_model;
public:
explicit ModelAdapter(QComboBox & box) : m_model(box.model()) {}
explicit ModelAdapter(QAbstractItemModel * model) : m_model(model) {}
QVariant operator[](int i) { return m_model->index(i, 0); }
};
// point of use
ModelAdapter model(myQComboBox);
for( auto i = 0; i < myQComboBox->count(); i++ )
{
result[i] = model[i];
}
With a good compiler, you can do the below and have it produce the same code as if you used combobox.model->index(i, 0) directly. I don't see the point of it, but hey, it's possible :)
// implementation
class Adapter {
QAbstractItemModel* m_model;
public:
explicit Adapter(QComboBox & box) : m_model(box.model()) {}
explicit Adapter(QAbstractItemModel * model) : m_model(model) {}
QVariant operator[](int i) { return m_model->index(i, 0); }
};
// point of use
for( auto i = 0; i < myQComboBox->count(); i++ )
{
result[i] = Adapter(myQComboBox)[i];
}
A similar adapter could provide you with iterators.
You can retrieve items data using the combo box's model. Here is an example, how I would do that:
QComboBox combo;
combo.addItem("Item 1");
QAbstractItemModel *model = combo.model();
QModelIndex idx = model->index(0, 0); // Refers to the first item
QString item = model->data(idx).toString(); // Returns 'Item 1'
To access the second and further items of the combo box, just change the row number in index() function call:
QModelIndex idx = model->index(0, 0);
the row number ^
I am not aware of any iterators based API for combo boxes so far, but you can use all the strength of QAbstractItemModel.

Extract menu action data in receiving function or slot

In my menu, I am setting data to the menu actions. How can I extract that data in my slot? Or even better, instead of connecting a slot, can I also connect a member function that is able to extract the action data (like in the 1st connect)? The action data is meant to identify each action. As a sidenode, I am not sure if I can use several menu action entries on only one openNote-action.
void Traymenu::createMainContextMenu() {
QAction *actionNewNote = m_mainContextMenu.addAction("Neue Notiz");
actionNewNote->setIcon(QIcon("C:\\new.ico"));
actionNewNote->setIconVisibleInMenu(true);
QObject::connect(actionNewNote,&QAction::triggered,this,&Traymenu::newNote);
QString menuEntryName;
QAction *openNote;
QVariant noteID;
for (int i = 0; i<m_noteList.count(); i++) {
std::string noteTitle = m_noteList[i].data()->getTitle();
menuEntryName = QString::fromStdString(noteTitle);
openNote = m_mainContextMenu.addAction(menuEntryName);
connect(openNote,SIGNAL(triggered()),this,SLOT(s_showNote()));
noteID.setValue(m_noteList[i].data()->getID());
openNote->setData(noteID);
}
m_mainIcon.setContextMenu(&m_mainContextMenu);
}
And the slot:
void Traymenu::s_showNote() {
QObject* obj = sender();
//int noteID = data.toInt();
//Search all notes in noteList for that ID and show it
}
Using QObject::sender()
You can use QObject::sender() to get the signal's sender, followed by qobject_cast to cast the sender pointer to the right type.
void Traymenu::s_showNote()
{
QAction* act = qobject_cast<QAction *>(sender());
if (act != 0)
{
QVariant data = act->data();
int noteID = data.toInt();
showNote(noteID); // isolate showNote logic from "get my ID" stuff
}
}
void Traymenu::showNote(int noteID)
{
// Do the real work here, now that you have the ID ...
}
As the Qt documentation warns, "This function violates the object-oriented principle of modularity." It's still a fairly safe and standard practice, though — just one with some shortcomings. In particular, note that you're committing to having a s_showNote method that only works when it's accessed as a slot (otherwise sender is 0).
Using QSignalMapper
Alternatively, you can use the QSignalMapper class to return a pointer to teh item or to associate a unique identifier (int or QString) with each item.
Something like this:
void Traymenu::createMainContextMenu()
{
signalMapper = new QSignalMapper(this); // (or initialize elsewhere)
// ... (create your newNote here same as before) ...
QString menuEntryName;
QAction *openNote;
int noteID;
for (int i = 0; i<m_noteList.count(); i++) {
std::string noteTitle = m_noteList[i].data()->getTitle();
menuEntryName = QString::fromStdString(noteTitle);
openNote = m_mainContextMenu.addAction(menuEntryName);
noteID = m_noteList[i].data()->getID();
openNote->setData(QVariant(noteID)); // (if you still need data in the QActions)
signalMapper->setMapping(openNote, noteID);
}
connect(signalMapper, SIGNAL(mapped(int)),
this, SLOT(showNote(int)));
m_mainIcon.setContextMenu(&m_mainContextMenu);
}
void Traymenu::showNote(int noteID) {
// Now you have the ID ...
}
This pattern has the benefit of isolating all the ugly "Wait, how do I get my identifier?" stuff in one spot, instead of having both the initialization code and the slot function having code for associating actions and IDs.
I would write it like:
void Traymenu::s_showNote() {
QObject* obj = sender();
QAction *action = qobject_cast<QAction *>(obj);
int id = action->data().toInt();
for (int i = 0; i < m_noteList.count(); i++) {
if (m_noteList[i].data()->getID() == id) {
[..]
}
}
}

Qt QMetaObjects and casting from QWidget to QObject

Im trying to make the code which reads xml files and deserialize various qt controls from this xml, and im doing this using QDomDocument, and i want to get the QLlist from my deserealization method. And im having a bit of troubles, here is some code of the template class (.h) file:
QList<T*> deserialize(QIODevice *input)
{
QList<T*> objects = QList<T*>();
if(_deserializeObject(input, objects))
return objects;
}
bool _deserializeObjects(QIODevice* input, QList<QObject*>& list);
and my .cpp file with deserialize method, here im reading the control tags from file:
bool Serializer::_deserializeObjects(QIODevice* input, QList<QObject *> &objects)
{
QDomDocument doc;
if (!doc.setContent(input))
return false;
QDomElement root= doc.documentElement();
for(int j = 0; j < root.childNodes().length();j++)
{
QObject* object;
qDebug() << root.tagName();
if(root.tagName().contains("QGroupBox")) // <------- Here i need to determine which control i need to process.
{
????
}
qDebug () << object->metaObject()->className();
qDebug() << object->metaObject()->propertyCount();
for(int i = 0; i < object->metaObject()->propertyCount(); i++)
{
object->metaObject()->cast()
QMetaProperty prop = object->metaObject()->property(i);
QString propName = prop.name();
if(propName == "objectName")
continue;
QDomNodeList nodeList = root.elementsByTagName(propName);
if(nodeList.length() < 1)
continue;
QDomNode node = nodeList.at(0);
QVariant value = object->property(propName.toLatin1().data());
QString v = node.toElement().text();
if(propName == "x")
{
x = v.toInt();
}
else if(propName == "y")
{
y = v.toInt();
}
else if(propName == "width")
{
width = v.toInt();
}
else if(propName == "height")
{
height = v.toInt();
}
if(propName == "geometry")
{
continue;
}
object->setProperty(propName.toLatin1().data(), QVariant(v));
}
object->setProperty("geometry",QVariant(QRect(x,y,width,height)));
objects.push_back(object);
}
return true;
}
In this part
if(root.tagName().contains("QGroupBox")) // <------- Here i need to determine which control i need to process.
{
????
}
qDebug () << object->metaObject()->className();
qDebug() << object->metaObject()->propertyCount();
for(int i = 0; i < object->metaObject()->propertyCount(); i++)
{
...
}
I want to actually somehow get the type of the control by name, so the question is, can i cast QGroupBox to QObject saving the QGroupBox properties so QObject metaObject class name would be QGroupBox, so i can pass all this properties? Because i don't want to make the loops for each control type. Also i when i got the result like so:
QList<QObject *> ds = s.deserialize<Object>((QIODevice*)&f);
Can i then just pass all QObjects in a loop and using QMetaObject class name and using qobject_cast cast each object to QPushButton,QLabel etc.?
QGroupBox is a subclass of QObject; therefore every QGroupBox is also a QObject, so you can treat it as one whenever you like. An explicit cast isn't necessary.
Iterating over all the diffent objects-derived-from-QObject in a loop will do what you want, provided that the methods you call on them are virtual methods (which they presumably will be -- in particular, QObject::metaObject() is a virtual method, so your loop will get the appropriate QMetaObject returned even if it is calling them method through a QObject pointer).
(As an aside, the annoying part of the process will probably be the part where you have read the name of the object's type from the XML and now need to instantiate an object of that type. AFAIK there is no good automatic way to do that in C++, so the best you can do is a factory function containing a giant switch statement with a separate case for every type you might want to instantiate)
Alternatively, use a right tool for a right job. Chances are that what you are really building here is some XML thing for defining widget layouts etc. Qt already has a tool for that, the Qt Designer which uses an XML format for the UI definitions and a C++ code generator for actually producing a C++ code during compile time.

Display array content in a List Widget Qt C++

i want to display some of the content of my array in a List Widget (item based) with Qt and C++, i tried this, but it dosent work :
QString exemple[2] = 'blablabla'
ui->listWidgetResult->addItem(exemple[2].toStdString().c_str());
Thanks !
This can't work:
QString example[2] = 'blablabla'
First, ' is for char values, not for strings. Second, you are declaring an array of two QStrings, but assign it to a C string. What you mean is perhaps this:
QString example[2] = {"blabla", "blabla"};
Which you can actually abbreviate to:
QString example[] = {"blabla", "blabla"};
To add each string of the array to your list widget, you need to add each one individually. Also, there's no need to convert to a C string. QListWidget::addItem() takes QStrings:
for (int i = 0; i < sizeof(example); ++i) {
ui->listWidgetResult->addItem(exemple[i]);
}
Or, if you have a recent compiler that supports C++-11:
for (const auto& str : example) {
ui->listWidgetResult->addItem(str);
}
Finally, instead of using plain arrays to hold your QStrings, you should instead consider holding them in a QStringList. You can then simply pass the whole QStringList using addItems().
I think this should be an easy solution to what you are asking for.
void MyClass::Set_List(QList<QString> filesList, int item_count)
{
QVector<QString> load_set(item_count);
for(int i = 0; i < item_count; i++)
{
load_set[i] = filesList[i];
ui -> listWidget -> addItem(load_set[i]);
}
}
Then to get the info back out...
void MyClass::Selection(QListWidgestItem * item)
{
for(int i = 0; i < item_count; i++)
{
if(ui -> listWidget -> item(i) == item)
{
str = ui -> listWidget -> item(i) -> text();
}
}
}