PDA

View Full Version : Making a picture loop cant get the results im looking for (XML + QMAP)



prophet0
28th November 2011, 15:47
Header File


QMap<QString, QString> parsePics(QXmlStreamReader& xml2);
void addElementDataToMap_Pics(QXmlStreamReader& xml2,
QMap<QString, QString>& map2) const;

void addPicsToUI(QList< QMap<QString,QString> >& root2);

void addToList_Pics(QList< QMap<QString,QString> >& root2);

QLabel *picture;


CPP File


QTimer::singleShot(0,this, SLOT(parseXML_Pics()));


QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(doAddToList_Pics()));
timer->start(3000);

void mediazone::doAddToList_Pics()
{
QList< QMap<QString,QString> > root2;
addToList_Pics(root2);
}

void mediazone::parseXML_Pics() {
/* We'll parse the example.xml */
QFile* file2 = new QFile("/home/dev/ttm/moives/xml/strings.xml");
/* If we can't open it, let's show an error message. */
if (!file2->open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(this,
"QXSRimpl::parseXML",
"file2 Couldn't open strings.xml",
QMessageBox::Ok);
return;
}
/* QXmlStreamReader takes any QIODevice. */
QXmlStreamReader xml2(file2);
QList< QMap<QString,QString> > root2;
/* We'll parse the XML until we reach end of it.*/
while(!xml2.atEnd() &&
!xml2.hasError()) {
/* Read next element.*/
QXmlStreamReader::TokenType token2 = xml2.readNext();
/* If token is just StartDocument, we'll go to next.*/
if(token2 == QXmlStreamReader::StartDocument) {
continue;
}
/* If token is StartElement, we'll see if we can read it.*/
if(token2 == QXmlStreamReader::StartElement) {
/* If it's named root, we'll go to the next.*/
if(xml2.name() == "root") {
continue;
}
/* If it's named row, we'll dig the information from there.*/
if(xml2.name() == "row") {
root2.append(this->parsePics(xml2));
}
}
}
/* Error handling. */
if(xml2.hasError()) {
QMessageBox::critical(this,
"xml2 has error",
xml2.errorString(),
QMessageBox::Ok);
}
/* Removes any device() or data from the reader
* and resets its internal state to the initial state. */
xml2.clear();
this->addToList_Pics(root2);

}

QMap<QString, QString> mediazone::parsePics(QXmlStreamReader& xml2) {
QMap<QString, QString> row2;
/* Let's check that we're really getting a row. */
if(xml2.tokenType() != QXmlStreamReader::StartElement &&
xml2.name() == "row") {
return row2;
}
/* Let's get the attributes for row */
QXmlStreamAttributes attributes2 = xml2.attributes();
/* Let's check that row has id attribute. */
if(attributes2.hasAttribute("id")) {
/* We'll add it to the map. */
row2["id"] = attributes2.value("id").toString();
}
/* Next element... */
xml2.readNext();
/*
* We're going to loop over the things because the order might change.
* We'll continue the loop until we hit an EndElement named row.
*/
while(!(xml2.tokenType() == QXmlStreamReader::EndElement &&
xml2.name() == "row")) {
if(xml2.tokenType() == QXmlStreamReader::StartElement) {
/* We've found Picture. */
if(xml2.name() == "zone2banner300x300") {
this->addElementDataToMap_Pics(xml2, row2);
emit doneParsePics();
}
}
/* ...and next... */
xml2.readNext();
}
return row2;
}

void mediazone::addElementDataToMap_Pics(QXmlStreamRead er& xml2,
QMap<QString, QString>& map2) const {
/* We need a start element, like <foo> */
if(xml2.tokenType() != QXmlStreamReader::StartElement) {
return;
}
/* Let's read the name... */
QString elementName2 = xml2.name().toString();
/* ...go to the next. */
xml2.readNext();
/*
* This elements needs to contain Characters so we know it's
* actually data, if it's not we'll leave.
*/
if(xml2.tokenType() != QXmlStreamReader::Characters) {
return;
}
/* Now we can add it to the map.*/
map2.insert(elementName2, xml2.text().toString());
}

void mediazone::addToList_Pics(QList< QMap<QString,QString> >& root2)
{
while(!root2.isEmpty()) {

QMap<QString,QString> row2 = root2.takeFirst();
QMapIterator<QString,QString> Iter_Pics(row2);
while(Iter_Pics.hasNext())
{
Iter_Pics.next();
QString myPics;
myPics = Iter_Pics.value();

QString vidpath_Pics = QApplication::applicationDirPath();
vidpath_Pics = "/home/dev/ttm/images/" + myPics;

picture = new QLabel(this);

picture->setGeometry(QRect(781,2,475,360));

picture->clear();
picture->setPixmap(QPixmap(vidpath_Pics));
picture->showFullScreen();
picture->show();

}

}

}


my code above does read from the xml file and grabs the picture text and then goes to the folder and grabs the file its supposed to rotate every 15 seconds starting with the first then when at the end do it again..

but it wont rotate nor start again if you reach the end...

please anyone can help thanks

d_stranz
28th November 2011, 15:58
Line 8, "doAddToList_Pics" always passes an empty map to the "addToList_Pics" method, so the method does nothing on timeout. And your timer is set for 3 seconds, not 15 seconds.

prophet0
28th November 2011, 16:18
how would i pass it to a slot then cause i cant pass the timer slot to addToList_Pics cause its not a slot :S how would i emit the timer to addToList_Pics

d_stranz
28th November 2011, 17:07
You need to make "root2" a member variable of your "mediazone" class, and remove lines 10 and 27 that create temporary QLists on the stack.

But your code has much bigger problems:

- Line 34 is a huge memory leak, because every time through the loop you create a new QLabel, but the old pointer is never deleted. What I think you want to do is simply *replace* the image on the label, not make a whole new label widget each time the image changes, right? Create the "picture" QLabel in the constructor and that's all you need (move line 34 to the constructor).

- The loop in lines 25 - 43 will execute almost instantly, so you will never see the pictures "rotating" through a sequence. The way your code is written now (new QLabel picture every time through the loop), you will get a whole bunch of QLabel instances on the screen, instantly, each with a different picture. If you move the code to create a new QLabel out of this loop and reuse the same QLabel, then you'll see a brief flicker as each picture is loaded and replaced, then the final picture will be displayed until the timer fires again.

So, you need to think about the logic of what you want to do. If the goal is to rotate through a series of pictures, and repeat the series every 15 seconds, then you need probably 2 timers: one to change from one picture to the next and leave it visible for a second or two, and another timer to repeat the whole thing every 15 seconds. If it takes more than 15 seconds to go through the whole list of pictures, then you have a problem, because the second timer will fire before the rotation is done. If it takes less than 15 seconds, then the last picture will stay up until the 15 second timer fires again.

If what you really want to do is to continuously rotate through the series of pictures, however long it takes, then you need one timer. Each time this timer fires, you display the next picture in the list. If you get to the end of the list, go back to the beginning again.

prophet0
28th November 2011, 17:13
If what you really want to do is to continuously rotate through the series of pictures, however long it takes, then you need one timer. Each time this timer fires, you display the next picture in the list. If you get to the end of the list, go back to the beginning again.

Yes this is what im trying to do create 1 label show 1 picture for 15 seconds then show the next one from xml once its finished start at the beginning...

im fairly new to Qt..

thanks for your prompt reply

d_stranz
28th November 2011, 18:08
Yes this is what im trying to do create 1 label show 1 picture for 15 seconds then show the next one from xml once its finished start at the beginning...


Then you need to do something like this:


- Load picture file names from XML into root2
- currentPictureIndex = 0
- Create QLabel
- Create timer
- Connect timer timeout to slot "showNextPicture"
- Start timer with 15000 ms timeout

- showNextPicture()
currentPictureIndex++
if ( currentPictureIndex == root2.size() )
currentPictureIndex = 0;
read picture at root2[ currentPictureIndex ]
show picture on label

But why not just load all the pictures into an array of QImage instead of repeatedly reading them from the files?

prophet0
28th November 2011, 18:11
i been trying various ways i have added root2 as a member of mediazone and changed a few things but cant really get a grasp on how to make it work correctly and clean no memory leaks :)

the files change all the time so the xml is created by a sql database and then the program reads from the xml but the program also downloads the files from the xml and then plays the pictures more information will be pulled from xml for this later on but this is how im trying to make it work for now

d_stranz
28th November 2011, 18:28
Then you want to change the "code" I posted to move the loading of pictures into a slot that gets called each time the XML changes. In that slot, you should



- stop the timer
- clear root2
- read new pictures into root2
- set currentPictureIndex = 0
- start the timer again


Stopping and restarting the timer may not be strictly necessary, but it ensures that you don't try to display a picture at an invalid index. In the "showNextPicture" slot, you should also check first to see if root2 is empty before trying to display a picture.

prophet0
28th November 2011, 18:30
im only stuck on the

read picture at root2[ currentPictureIndex ]
show picture on label

in the slot shotNextPicture

d_stranz
28th November 2011, 18:48
void showNextPicture()
{
if( !root2.isEmpty() )
{
QMap<QString,QString> row2 = root2.takeFirst();
if ( !row2.isEmpty() )
{
currentPictureIndex++;
if ( currentPictureIndex == row2.size() )
currentPictureIndex = 0;

QMapIterator<QString,QString> Iter_Pics(row2) + currentPictureIndex;

QString myPics;
myPics = Iter_Pics.value();

QString vidpath_Pics = QApplication::applicationDirPath(); // why are you doing this
vidpath_Pics = "/home/dev/ttm/images/" + myPics; // when this trashes whatever you just assigned above?

picture->setGeometry(QRect(781,2,475,360)); // why are you setting this geometry

picture->clear();
picture->setPixmap(QPixmap(vidpath_Pics));
picture->showFullScreen(); // only to then show it full screen?
picture->show();

}
}
}


Obviously untested, but you should be doing something like this.

prophet0
28th November 2011, 18:53
line 17: thought it was necessary

fullscreen i was meaning to remove that due to the fact i setgeometry

QMapIterator<QString,QString> Iter_Pics(row2) + currentPictureIndex;

mediazone.cpp:315: error: expected ‘,’ or ‘;’ before ‘+’ token

d_stranz
28th November 2011, 19:37
Replace:

QMapIterator<QString,QString> Iter_Pics(row2) + currentPictureIndex;

with:


QMap<QString, QString>::const_iterator Iter_Pics = row2.begin() + currentPictureIndex;


line 17: thought it was necessary

Necessary for what? You retrieve the application directory path and assign it to vidpath_Pics in line 17. In the very next line (18), you assign vidpath_Pics to a different string. So the first assignment doesn't do anything useful. If what you intend to do is to assign vidpath_Pics to the concatenation of your application directory path + the current picture file name, then you want this:


QString vidpath_Pics = QApplication::applicationDirPath() + myPics;

and delete line 18.

prophet0
28th November 2011, 19:44
ok so i did fix those lines

but it was returning empty i added the code into my addToList_Pics with the doAddToList_Pics slot and its working with the time new value everytime ... only the label nothing appears .. i think i messed up the qlabel in the constructor


in my cpp i have

picture = new Qlabel(this);

then in the slot i have everything to show but maybe not getting passed ?

again thank you for your help

d_stranz
28th November 2011, 19:53
Your constructor needs this:


picture = new QLabel(this);

and make sure "picture" is a member variable for your mediazone class.

Are you sure that the picture actually gets loaded from the file? Try changing it to this:



QPixmap pixmap;
if ( pixmap.load( vidpath_Pics ) )
picture->setPixmap( pixmap );
else
// error, pixmap didn't load properly

prophet0
28th November 2011, 19:58
After changing the code to yours application starts then exits

QLabel *picture;
is in my header file for mediazone yes

d_stranz
28th November 2011, 20:10
Are you running this in a debugger? Obviously something is crashing somewhere, but if you are making a release mode executable and running that, you can never find out where. Make a debug version, run it in the debugger, see where the crash occurs, and figure out why.

If you aren't calling "picture = new QLabel( this )" in your constructor, then "picture" will be uninitialized when you try to use it, and will cause a crash. Putting the declaration in the header file doesn't create the QLabel instance, it just declares the variable. Show the code in your constructor that create the QLabel.

prophet0
28th November 2011, 20:19
its saying vidpath_pics is not accessable

d_stranz
28th November 2011, 20:29
I don't have any idea what you're talking about now. First you said the app was crashing, now you tell me there's a compiler error. I have no idea what you have changed or where.

If the compiler is telling you that some variable is not accessible, then it means you have declared the variable outside the scope of where you are trying to use it (or have not declared it at all). And in your last note, you said "vidpath_pics", but in your original code, it was "vidpath_Pics". Did you simply make a typo here or in your code?

prophet0
28th November 2011, 21:32
this is what the debugger has to say when it segfault



Locals
Iter_Pics
myPics ""
root2 <9 items>
row2 <1 items>
this
QFrame
_layout
o 0x0
aio_audioOutput
currentPictureIndex -1225357119
mediaObject
metaInformationResolver
myPics ""
nam
picture
root2 <9 items>
sources <0 items>
ui
wid_videowidget
xml
d_ptr
vidpath_Pics <not accessible>
Watchers


Added after 6 minutes:

sorry i ment vidpath_Pics

Added after 4 minutes:

So i commented this line

//QString myPics;
//myPics = Iter_Pics.value();

and the application runs as it should ..... something went wrong here :S

Added after 41 minutes:



QString myPics;
myPics = Iter_Pics.value();


is causing the problem and i have no idea why

when i start my application it starts grey then segfault

when i start with the lines comment out application works as normal!!

hope this helps ....

d_stranz
28th November 2011, 21:40
Looking at debugger output doesn't tell me anything about your *code*. And if you aren't retrieving anything into "myPics" from your "row2" iterator, but you say your application runs as it should, then I have no idea what you are actually doing in the code. It has probably changed a lot from your original post.

Your original code had "myPics" containing the name of a picture file. If you are now ignoring the name, how does the picture get retrieved from the file?

I can't help you without seeing what you are doing in the code.

prophet0
29th November 2011, 16:02
my code is as fallows

.h




namespace Ui {
class mediazone;
}

class mediazone : public QFrame
{
Q_OBJECT

public:
explicit mediazone(QWidget *parent = 0);
~mediazone();

public slots:
void parseXML_Pics();
void doAddToList_Pics();

signals:
void doneParsePics();

private:
Ui::mediazone *ui;
QXmlStreamReader xml;
QXmlStreamReader xml2;

QList< QMap<QString,QString> > root2;
QString myPics;

QMap<QString, QString> parsePics(QXmlStreamReader& xml2);
void addElementDataToMap_Pics(QXmlStreamReader& xml2,
QMap<QString, QString>& map2) const;

void addPicsToUI(QList< QMap<QString,QString> >& root2);

void addToList_Pics(QList< QMap<QString,QString> >& root2);

int currentPictureIndex;
QLabel *picture;

};

#endif // MEDIAZONE_H


.cpp


mediazone::mediazone(QWidget *parent) :
QFrame(parent),
ui(new Ui::mediazone)
{
//Start parseXML as soon as this application loads
QTimer::singleShot(0,this, SLOT(parseXML_Pics()));

//when parseXML is complete doAddToList
//connect(this, SIGNAL(doneParsePics()), SLOT(doAddToList_Pics()));

ui->setupUi(this);
ui->view->hide();

picture = new QLabel(this);

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(doAddToList_Pics()));
timer->start(15000);
}

mediazone::~mediazone()
{
delete ui;
}

void mediazone::doAddToList_Pics()
{
addToList_Pics(root2);
}

void mediazone::parseXML_Pics() {
/* We'll parse the example.xml */
QFile* file2 = new QFile("/home/dev/ttm/moives/xml/strings.xml");
/* If we can't open it, let's show an error message. */
if (!file2->open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(this,
"QXSRimpl::parseXML",
"file2 Couldn't open strings.xml",
QMessageBox::Ok);
return;
}
/* QXmlStreamReader takes any QIODevice. */
QXmlStreamReader xml2(file2);
/* We'll parse the XML until we reach end of it.*/
while(!xml2.atEnd() &&
!xml2.hasError()) {
/* Read next element.*/
QXmlStreamReader::TokenType token2 = xml2.readNext();
/* If token is just StartDocument, we'll go to next.*/
if(token2 == QXmlStreamReader::StartDocument) {
continue;
}
/* If token is StartElement, we'll see if we can read it.*/
if(token2 == QXmlStreamReader::StartElement) {
/* If it's named root, we'll go to the next.*/
if(xml2.name() == "root") {
continue;
}
/* If it's named row, we'll dig the information from there.*/
if(xml2.name() == "row") {
root2.append(this->parsePics(xml2));
}
}
}
/* Error handling. */
if(xml2.hasError()) {
QMessageBox::critical(this,
"xml2 has error",
xml2.errorString(),
QMessageBox::Ok);
}
/* Removes any device() or data from the reader
* and resets its internal state to the initial state. */
xml2.clear();
this->addToList_Pics(root2);

}

QMap<QString, QString> mediazone::parsePics(QXmlStreamReader& xml2) {
QMap<QString, QString> row2;
/* Let's check that we're really getting a row. */
if(xml2.tokenType() != QXmlStreamReader::StartElement &&
xml2.name() == "row") {
return row2;
}
/* Let's get the attributes for row */
QXmlStreamAttributes attributes2 = xml2.attributes();
/* Let's check that row has id attribute. */
if(attributes2.hasAttribute("id")) {
/* We'll add it to the map. */
row2["id"] = attributes2.value("id").toString();
}
/* Next element... */
xml2.readNext();
/*
* We're going to loop over the things because the order might change.
* We'll continue the loop until we hit an EndElement named row.
*/
while(!(xml2.tokenType() == QXmlStreamReader::EndElement &&
xml2.name() == "row")) {
if(xml2.tokenType() == QXmlStreamReader::StartElement) {
/* We've found Picture. */
if(xml2.name() == "zone2banner300x300") {
this->addElementDataToMap_Pics(xml2, row2);
emit doneParsePics();
}
}
/* ...and next... */
xml2.readNext();
}
return row2;
}

void mediazone::addElementDataToMap_Pics(QXmlStreamRead er& xml2,
QMap<QString, QString>& map2) const {
/* We need a start element, like <foo> */
if(xml2.tokenType() != QXmlStreamReader::StartElement) {
return;
}
/* Let's read the name... */
QString elementName2 = xml2.name().toString();
/* ...go to the next. */
xml2.readNext();
/*
* This elements needs to contain Characters so we know it's
* actually data, if it's not we'll leave.
*/
if(xml2.tokenType() != QXmlStreamReader::Characters) {
return;
}
/* Now we can add it to the map.*/
map2.insert(elementName2, xml2.text().toString());
}

void mediazone::addToList_Pics(QList< QMap<QString,QString> >& root2)
{
if( !root2.isEmpty() )
{
QMap<QString,QString> row2 = root2.takeFirst();
if ( !row2.isEmpty() )
{
currentPictureIndex++;
if ( currentPictureIndex == row2.size() )
currentPictureIndex = 0;

QMap<QString, QString>::const_iterator Iter_Pics = row2.begin() + currentPictureIndex;

QString mPics;
mPics = Iter_Pics.value();

QString vidpath_Pics = QApplication::applicationDirPath() + myPics;

picture = new QLabel(this);
picture->setGeometry(QRect(781,2,475,360));

picture->clear();
picture->setPixmap(QPixmap(vidpath_Pics));
picture->show();

}
}


}

d_stranz
29th November 2011, 19:04
I think you are really confused about what you are trying to do. Why don't you take a break and write down (in words, not code) what it is that you want your application to do. Then, compare that description with your code and try to understand why it is that your code does not do what you have written.

Hint: Lines 148 / 149 (the CODE tags only show 2 digits, so it wraps after line 99) in the cpp file is supposed to retrieve the picture at the current index from your "row2". It does, but puts it into a local variable called "mPics" (which is then ignored). Then you use an empty QString member variable ("myPics") in line 151 to create the name of the picture file to load. Of course, this file name is nonsense, because all you have is the application directory.

I think one source of your confusion is that you are mixing up parsing XML, interacting with a database, and displaying pictures at 15 second intervals. All of this code mixed together like that makes it very hard to see the logic the program is following.

From what I can understand, you have three separate functions, and none of them are rally related to each other:

1. A database periodically sends an updated result about picture file names
2. The picture file names need to be parsed out of the XML string
3. The list of pictures needs to be displayed in screen-saver mode

Each of these is a completely separate job. Job 1 simply needs to listen for changes coming from a database. It doesn't need to care at all about what the string contains. Job 2 needs to parse XML strings into a list of file names. It doesn't care where the XML comes from, it just knows that when it gets a new one, it has to pull file names out of it. Job 3 is responsible for displaying pictures from files. Each time it gets a new list of file names, it starts displaying them. It doesn't care where the names come from, it just waits for someone to tell it "here are new files to display".

So, I would break the "mediazone" class into at least 3 new classes, one to handle each job:

- a "databaseMonitor" class whose only job is to wait for the database to send a new result. When it receives a new result, it sends a signal:

void newDatabaseResult( const QString & databaseXML )

This signal contains the unparsed string the the database sent.

- a "pictureListParser" class whose only job is to parse the XML into a list of picture file names.

It has a slot:

void onNewXMLString( const QString & pictureXML )

and a signal:

void newPictureList( const QStringList & pictureFiles )

The onNewXMLString slot is connected to the databaseMonitor::newDatabaseResult() signal. In this slot, the XML is parsed to build a QStringList of picture file names. After the parsing is done, the signal newPictureList() is emitted with the list of file names.

- a "pictureDisplay" class whose only job is to display the list of pictures at the interval set by the timer.

It has two slots:

void onNewPictureList( const QStringList & pictureFiles )

which is connected to the signal from the "pictureListParser" class and simply updates the list of picture files for display, and

void showNextPicture()

which is connected to the timer's timeout signal. The slot's job is to 1) increment the "currentPictureIndex" and 2) read the picture from the file name stored in the QStringList at that index.

prophet0
30th November 2011, 14:50
d_stanz,

Thanks for the information and taking the time to look into my situation i will do as you say and if i resolve or have further questions i will post my results

thanks again

if you have any good material to learn Qt to the fullest please let me know ..