PDA

View Full Version : Problem showing the right thing.



Archa4
11th February 2011, 08:28
So i had a problem showing the right data after parsing xml. I found a way, but now i need help from u guys:
My main.cpp calls for list.cpp (here is the important part):

List::List(bool first, QWidget *parent) :
QWidget(parent)
{
if(first == true)
{
QString str = "http://www.forumcinemas.lv/rss/xml/movies/";
Download_xml *Other= new Download_xml(str);
this->hide();
}
else
{
layout = new QVBoxLayout(this);
listWidget = new QListWidget(this);
layout->addWidget(listWidget);
this->setFocus();
}

As u can see, i call for download_xml, where the xml parsing will take place.
Here is download_xml.cpp part.

void Download_xml::parseXml()
{
QVector<Movie *> movie_list;
List *list = new List(false);

...

_ml = movie_list;
list->completeList(_ml);
}

and here is the list.cpp function part:

void List::completeList(QVector<Movie *> ml)
{

QVector<Movie *> new_ml = ml;
for (int i = 0; i<new_ml.size(); i++)
{
listWidget->insertItem(i, "");
listWidget->setItemWidget(listWidget->item(i), new Custom_Widget
(new_ml[i]->name(), new_ml[i]->date(), new_ml[i]->about(), new_ml[i]->picture()));
listWidget->item(i)->setSizeHint(QSize (350,470));
listWidget->setSpacing(1);
}

The problem is, i can't see the list that is created after the parsing in XML takes place. I run through the debug, and i think that the list is created and even shown, but in background. In front all u can see is the first list, that is created empty.
That's why I ask your help: either to show the list, that is created after parsing in front, or help me rewrite the code in some way it would work better...

wysota
11th February 2011, 08:39
What is the point of copying the vector here and there? And why are you hiding your widget in the first code snippet you posted?

Archa4
11th February 2011, 08:43
That was one of the things i tried to hide the first list created...

FelixB
11th February 2011, 08:47
What is the reason for "QVector<Movie *> new_ml = ml;" in "completeList()"?

I'm not sure, if I understand your code. First, you create a "List" with parameter "first==true", then the xml parsing is done. During this parsing, in "parseXml()" a new "List" is created with "first==false". Then you call "completeList()", where you display the elements of the parameter "ml", which is "movie_list" which is "_ml" from the first list. Is that correct?

Why do you create several "List"-objects?

Archa4
11th February 2011, 08:53
You are almost correct:
"you display the elements of the parameter "ml", which is "movie_list" which is "_ml" from the first list", they are not from the first list, but from download_xml.
First list does not contain any items. It's empty

About why did i do that - I couldn't find other way around it. If you can suggest any other way (for example, start with download_xml, and then show the list), i would really appreciate it

wysota
11th February 2011, 08:58
Why don't you just populate the list you have instead of creating a new one?

Archa4
11th February 2011, 09:01
That was the first thing i tried. Problem is - i cannot call the mexthod completeList from Download_xml, without an object. And I have no idea how to call this method for the list that has already been created... Can u help me with code?

wysota
11th February 2011, 09:11
Just return a list of QListWidgetItem objects and then put them into the list when you have access to the list.

FelixB
11th February 2011, 09:12
what about something like this (untested):


void function()
{

QVector<Movie*> vec = ParseXml("http://www.forumcinemas.lv/rss/xml/movies/");
QListWidget* listWidget = new QListWidget();
FillListWidget(listWidget, vec);

listWidget->show();
}

Archa4
11th February 2011, 09:27
See here is a problem: i cannot return this list into constructor. The reason - i have to end the List constructor for parsingXML to finish. But if I end the constructor,then I have no idea how to return the list from Download_xml to the List class...


what about something like this
I cannot call ParseXml, because in the Download_xml there are 2 signals that have to appear before parseXML is called. If i just try ParseXml, it will parse an empty XML, that has not been filled, cause the ReadyRead signal has not appeared yet...

Added after 10 minutes:

You are NEVER gonna believe this - i made it work :D
I made a layout in the first list that was created and added the newly created Download_xml widget Other into it.
Then I created a layout in Download_xml and added the newly created List widget list into it.
So now I am showing a QListWidget in List Widget in Download_xml Widget in List Widget.
Just LOL.
I would really like to improve and simplify my code, so suggestions are very welcome

FelixB
11th February 2011, 09:29
forget the thing with this constructor. Do you really need an own QWidget? does List have any ui things except the ListWidget?


I cannot call ParseXml, because in the Download_xml there are 2 signals that have to appear before parseXML is called. If i just try ParseXml, it will parse an empty XML, that has not been filled, cause the ReadyRead signal has not appeared yet...

ok. call a function "GetMovieVector()" which does everything that's necessary to create your movielist.


Vector<Movie*> GetMovieVector(const QString& file)
{
Download_xml Other(str);
return Other.GetMovieList();
}

wysota
11th February 2011, 09:36
Let's go back to the beginning. Please state what is the ultimate result you want to achieve and describe (without posting code) how you currently do it.

Archa4
11th February 2011, 09:47
I need to create a list of customized_widgets. Right now I have an List class, where i want to show the list with it's widgets, i have an Download_xml class where i connect to the URL and parse the XML, i have an Object class Movie where i create new objects with data i get from Download_xml and I have Customize_widget class where i create the widgets using the data from QVector<Movie *> where all the data about those list items is stored.

The Ultimate Problem i have - when i create a variable Download_xml like this:
Download_xml *Other= new Download_xml(str);
I cannot get any data from it for now, cause no signals are emitted (in particular the ReadyRead and requestFinished), that's why i cannot get any data from there. I found a way around this:
I call a function from within the Download_xml, AFTER the signals were emitted and parseXML has finished it's job.


forget the thing with this constructor. Do you really need an own QWidget? does List have any ui things except the ListWidget?



ok. call a function "GetMovieVector()" which does everything that's necessary to create your movielist.


Vector<Movie*> GetMovieVector(const QString& file)
{
Download_xml Other(str);
return Other.GetMovieList();
}

And what am I supposed to write in GetMovieList?
after:
Download_xml Other(str);
i cannot access any data from that Other, couse there is no signals emitted, cause the {...} block has not ended.
But without the signals parseXML wont start...
No signals -> no parseXML -> no data...

wysota
11th February 2011, 10:09
How does download_xml work? You create the object on the stack so unless the whole download is synchronous, it will go out of scope and be deleted.

Archa4
11th February 2011, 10:14
Em... I have no idea what u just said :) sorry i'm really noob. I found out there is such a thing as Qt less then 2 weeks ago... I can post the Download_xml.cpp here:


#include <QtCore>
#include <QtGui>
#include <QtNetwork>
#include "download_xml.h"
#include "movie.h"
#include "list.h"

Download_xml::Download_xml(QString givenAddress, QWidget *parent) : QWidget(parent)
{

//OS_Symbian
#ifdef Q_OS_SYMBIAN
// Set Internet Access Point
QNetworkConfigurationManager manager;
const bool canStartIAP = manager.capabilities() & QNetworkConfigurationManager::CanStartAndStopInter faces;

// Is there default access point, use it
QNetworkConfiguration cfg = manager.defaultConfiguration();
if (!cfg.isValid() || !canStartIAP)
{
// Available Access Points not found
QMessageBox::warning(this, "Error", "No access point");
return;
}

m_session = new QNetworkSession(cfg);
m_session->open();
m_session->waitForOpened();
#endif

connect(&http, SIGNAL(readyRead(QHttpResponseHeader)),
this, SLOT(readData(QHttpResponseHeader)));

connect(&http, SIGNAL(requestFinished(int,bool)),
this, SLOT(finished(int,bool)));
fetch(givenAddress);
layout = new QVBoxLayout(this);

}

void Download_xml::fetch(QString address)
{
xml.clear();
QUrl url(address);
http.setHost(url.host());
connectionId = http.get(url.path());
}

void Download_xml::readData(const QHttpResponseHeader &resp)
{
if (resp.statusCode() != 200)
http.abort();
else
{
xml.addData(http.readAll());
}
}

void Download_xml::finished(int id, bool error)
{
if (error)
{
QMessageBox::warning(this, "Error", http.errorString());

}
else if (id == connectionId)
{
parseXml();
}


}

void Download_xml::parseXml()
{
bool movies = false;
bool rss = false;
bool movie = false;
bool item = false;
QVector<Movie *> movie_list;
List *list = new List(false);

while (!xml.atEnd() && movies == false && rss == false)
{
xml.readNext();
if (xml.isStartElement())
{
currentTag = xml.name().toString();
if (currentTag == "movieList")
movies = true;
if (currentTag == "rss")
rss = true;
}
}
while (!xml.atEnd() && movies == true)
{
xml.readNext();
if (xml.isStartElement())
{
currentTag = xml.name().toString();
if (currentTag == "movie")
movie = true;
}
else if (xml.isEndElement() && movies == true && movie == true)
{
if (xml.name() == "movie")
{
Movie *one_movie = new Movie;
one_movie->setName(titleString);
one_movie->setDate(dateString);
one_movie->setAbout(aboutString);
one_movie->setPicture(pictureString);
movie_list.append(one_movie);
titleString.clear();
dateString.clear();
aboutString.clear();
pictureString.clear();
movie = false;

}

}
else if (xml.isCharacters() && !xml.isWhitespace() && movies == true && movie == true)
{
if (currentTag == "title")
titleString += xml.text().toString();
else if (currentTag == "globalReleaseDate")
dateString += xml.text().toString();
else if (currentTag == "annotation")
aboutString += xml.text().toString();
else if (currentTag == "imageType1")
pictureString += xml.text().toString();
}
}




while (!xml.atEnd() && rss == true)
{
xml.readNext();
if (xml.isStartElement())
{
currentTag = xml.name().toString();
if (currentTag == "item")
item = true;
}
else if (xml.isEndElement() && rss == true && item == true)
{
if (xml.name() == "item")
{
Movie *one_movie = new Movie;
one_movie->setName(titleString);
one_movie->setDate(dateString);
one_movie->setAbout(aboutString);
movie_list.append(one_movie);
titleString.clear();
dateString.clear();
aboutString.clear();
item = false;
}

}
else if (xml.isCharacters() && !xml.isWhitespace() && rss == true && item == true)
{
if (currentTag == "title")
titleString += xml.text().toString();
else if (currentTag == "pubDate")
dateString += xml.text().toString();
else if (currentTag == "description")
aboutString += xml.text().toString();
}
}

if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError)
{
qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString();
http.abort();
}
_ml = movie_list;
layout->addWidget(list);
list->completeList(_ml);
}

Download_xml::~Download_xml()
{
}

FelixB
11th February 2011, 10:18
How does download_xml work? You create the object on the stack so unless the whole download is synchronous, it will go out of scope and be deleted.

no, that was my (stupid) idea. I have no clue how this xml download-stuff works... Archa4 creates it on the heap, as shown in the first post.

Archa4
11th February 2011, 12:09
I was able to alter mine cod a bit, so i don't have to use 3 layouts now.
When parsing finishes I emit a signal, that launches the function completeList.
I had to declare in List.h

Download_xml *Other;

and in list constructor I added the connect thing:

connect(Other, SIGNAL(finish()),this, SLOT(completeList()));

Now i don't have to return the QVector<Movie *>, i just get it from Other variable.

Still if there are any suggestions on further code improvement, post here!

wysota
11th February 2011, 12:17
Oh, the class uses QHttp. I suggest to use QNetworkAccessManager instead. Also don't read data incrementally from the response, wait until the reply is done and read all in one go.

Archa4
11th February 2011, 12:25
What improvements does QNetworkAccessManager has, that would help me improve my project?

wysota
11th February 2011, 12:31
Countless :) From developer's point of view it lets you reduce your code significantly and make it less error prone.

Archa4
11th February 2011, 12:55
Thanks, I'll try to alter my code :)