Qt ComboBox + Searching XML + Printing out data from XML to GUI - c++

Update
void MainWindow::readXml()
{
QDomDocument Champions;
QFile xmlFile("champions.xml");
xmlFile.open(QIODevice::ReadOnly);
Champions.setContent(&xmlFile);
QDomElement root = Champions.firstChildElement();
QDomNodeList ChampionNames = root.elementsByTagName("Champion");
for(int i = 0; i < ChampionNames.count(); i++)
{
QDomNode championNode = ChampionNames.at(i);
if(championNode.isElement())
{
QDomElement Champion = championNode.toElement();
ui->comboBox->addItem(Champion.attribute("Name"));
}}}
Managed to get something like this so I've the names in the comboBox now :)
I'm new to this community so I'm happy to meet all of You!
First I want to inform You that I'm pretty new to Qt programming however I've had some basic c++ lessons in school though it was only console programming and I've never worked on stuff like that. Why I've started with Qt? It looked easy to me and hell it was compared to visual studio! So here is my problem.
Basiclly I have a comboBox where I would read my "Name="" " attributes in. There will be around 100 maybe abit less maybe a bit more don't know Yet. I don't know how to start with everything this since I have never done such thing before. What i want the software to do is basiclly when i select a "name" in the combobox, i want all the attributes ("Q" "W" "E" "R") to be printed out in the 4 labels as You can see on the little image I've added.
I don't know if I need to first read the file into some strings arrays or data structures. Do I need to search the XML file for the "Name" which is selected in ComboBox and then somehow print it out? I spent some time on this but I cant find a way to achive the thing I want. I would really appriciate some code exemples specially using the ComboBox since as said I'm new to this.
XML File looks like this in case image is blurry:
<ROOT>
<Champ Name="XXX1">
<Q>QQ1</Q>
<W>WW1</W>
<E>EE1</E>
<R>RR1</R>
</Champ>
<Champ Name="XXX2">
<Q>QQ2</Q>
<W>WW2</W>
<E>EE2</E>
<R>RR2</R>
</Champ>
</ROOT>
I'm really bad on describing things so I've made a small ilustration using a pen to let You understand me better :)
My beautiful sketch.
Thanks for Your support in advance! Hope I'm clear enought with my question. Have a great day.

First you should represent your XML data as a C++ class/struct:
class Champ {
public:
// A constructor using QDomElement as argument
Champ(QDomElement element);
QString name;
QString q, w, e, r;
};
Second you should load the XML file, parse it, and populate a vector (or map) with Champ objects.
QVector<Champ> loadChampsObjects(const QString& xmlPath)
{
QVector<Champ> champObjects;
QFile file(xmlPath);
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
return champObjects;
/* QDomDocument takes any QIODevice. as well as QString buffer*/
QDomDocument doc("mydocument");
if (!doc.setContent(file))
return champObjects;
//Get the root element
QDomElement docElem = doc.documentElement();
// get the nodes we need
QDomNodeList nodeList = docElem.elementsByTagName("champ");
// Check each node create a Champ object and add it the vector...
for(int i = 0; i < nodeList.count(); i++)
champObjects.append(Champ(nodeList.at(i).toElement()));
return champObjects;
}
Third populate the QComboBox. We will use the index in vector as userData:
QVector<Champ> champObjects = loadChampsObjects("path.to.xml");
for (unsigned i=0; i<champObjects.count(); i++)
pComboBox->addItem(champObjects[i].name, QVariant(i));
Finally in the slot connected to the currentIndexChanged signal of combo box you can easily access the properties of the selected object using the userData that indicated the index in the vector:
void champObjectChanged()
{
unsigned vectorIndex = pComboBox->itemData(pComboBox->currentIndex())->toInt();
Champ c = champObjects[vectorIndex];
// do whatever you want with it
}

Use Qt's DOM components to do all the parsing and tree building for you (see the examples here for their use).
For the QComboBox, once the QDomDocument is complete you can search for all your Champ nodes and read the Name attributes off of them. For each one, just use QComboBox::addItem(const QString& text) to add them.
Then connect the QComboBox::currentIndexChanged (const QString&) signal to a method in your XML handling class that searches the DOM tree for a Champ node with a Name attribute that matches it. Once found read off each one of it's child node values (that's the 'Q', 'W', 'E', 'R' values).

Related

Extract geometric objects (lines, circles,...) from a pdf using PDFMM

I have a PDF containing several geometric objects (mostly lines) in different sizes and color. I want to extract them in the following form, e.g. for lines:
(startx, starty)
(endx, endy)
width
color
Optinal a "z" Position determining which object is drawn first. The language of my choice is C++ and I thought about PoDoFo, respectively PDFMM, as it should be more accessible. However I am total lost how to acess this information...
I found the following reference:
PDF parsing in C++ (PoDoFo)
however I was not able to make the PdfTokenizer work. The Tokenizer.TryReadNextToken needs a InputStreamDevice object, and I do not know how to get it.
For example: I create a single page with just one line in pdfmm. And now I want to extract this information:
#include <pdfmm/pdfmm.h>
int main()
{
try {
PdfMemDocument document;
document.Load("test.pdf");
PdfPage* page = document.GetPages().CreatePage(PdfPage::CreateStandardPageSize(PdfPageSize::A4));
// Draw single line
PdfPainter painter;
painter.SetCanvas(page);
painter.GetGraphicsState().SetLineWidth(10);
painter.DrawLine(0, 0, page->GetRect().GetWidth(), page->GetRect().GetHeight());
painter.FinishDrawing();
// Loop over all token of page
PdfTokenizer token(true);
char* stoken = nullptr;
PdfVariant var;
PdfContentType type;
while (token.TryReadNextToken( ???? ,stoken,type)) {
}
}
catch (PdfError& err)
{
err.PrintErrorMsg();
return (int)err.GetError();
}
}
If anybody could push me in the correct direction, this would be awesome! And if somebody has a good documentation about the structure of a pdf and/or a good tutorial of pdfmm / PoDoFo, this would also highly appreciated...

Parse XML in qt and get tree tag structure

I need to parse an XML file in c++(!11)/ QT into a vector holding each value and its xml tag parent structure.
I'm new to QT and I know there are some good options in their library. However, much of what I have found focuses on those who know the tag names ahead of time. For me I need something more generic. The tag names (and values) are irrelevant for my purpose and could be anything, my focus is on the tag structure holding each value. What is the best approach to take for this? QDomDocument?
*Note: The actual xmls will be much more complex in tree structure length.
Example input
Test.xml
<MainTag>
<description>Test Description</description>
<type>3</type>
<source>
<description>Source test Description1</description>
<type>4</type>
</source>
<source>
<description>Source test Description2</description>
<type>5</type>
<name>
<element>1</element>
</name>
</source>
</MainTag>
Example Output
(string rows contained in c++ vector):
description=Test Description
type=3
source.description=Source test Description1
source.type=4
source.description=Source test Description2
source.type=5
source.name.element=1
When parsing XML files I find more flexible navigating the DOM of the XML than using an stream parser because your code is less aware of the order of the elements and focuses more on the structure and content.
For DOM navigation you can use the QDomDocument and related classes:
first init a document with the content of your XML file using QDomDocument::setContent
extract the document element (the parent element of your XML)
the rest consist on navigating the structure using QDomElement::firstChildElement(const QString& tagName) and QDomElement::nextSiblingElement(const QString& tagName) (QDomElement inherits from QDomNode); as you don't know the tag name, just keep it empty and it will return all the elements at that level
to extract the content of a tag you can use QDomNode::nodeValue, the trick here is to extract the value of the first node that is not an element (see code below)
although not mentioned in your question, if you need to access attributes you have QDomElement::attribute
Example code for parsing an unknown XML
This code parses the XML and extract tag names as well as its text. It doesn't extract attributes nor empty nodes.
Note: I've corrected closing tag from given example <MainTag> to </MainTag>.
#include <QtXml>
#include <QtCore>
#include <vector>
#include <iostream>
// Recursive function to parse the XML
void parseXML(const QDomElement& root, const QString& baseName, std::vector<QString>& v)
{
// Extract node value, if any
if (!baseName.isEmpty() && !root.firstChild().nodeValue().isEmpty()) { // the first child is the node text
v.push_back(baseName + "=" + root.firstChild().nodeValue());
}
// Parse children elements
for (auto element = root.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) {
parseXML(element, baseName + "." + element.tagName(), v);
}
}
int main(int argc, char* argv[])
{
const QString content = "<MainTag>"
"<description>Test Description</description>"
"<type>3</type>"
"<source>"
" <description>Source test Description1</description>"
" <type>4</type>"
"</source>"
"<source>"
" <description>Source test Description2</description>"
" <type>5</type>"
" <name>"
" <element>1</element>"
" </name>"
"</source>"
"</MainTag>";
std::vector<QString> v;
QDomDocument xml("xml");
xml.setContent(content);
parseXML(xml.documentElement(), "", v); // root has no base name, as indicated in expected output
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << it->toStdString() << std::endl;
}
return 0;
}
DOM from file
To populate the DOM from a file change the setContent line with something like code below (error checking omitted for simplicity):
QFile file(filePath);
file.open(QFile::ReadOnly);
xml.setContent(file.readAll());

C++ Builder - Copy a Link to a String Based On Certain Words in It

This's my first post here.
I have 4 links in a Memo or TStringList:
http://website.com/text/book&id=20/programming
http://website.com/text/book&id=10/programming
http://website.com/text/book&id=40/programming
http://website.com/text/book&id=30/programming
What I want to do is to copy each link based on its "id number", as shown above to separate string, and NOT based on its
index in TMemo or in TStringList.
This's because the id number of each link is always change every time or dynamics (it's a random id).
So let's say :
if the link containts id=10 then it should be copied to a string called "id10".
final result is id10 containts: http://website.com/text/book&id=10/programming
if the link containts id=30 then it should be copied to a string called "id30".
final result is id30 containts: http://website.com/text/book&id=30/programming
so on and on until all links are copied into a separate different string.
I've tried to resolve this problem using substring function, such as:
int p = 0;
String id10, id20, id30, id40;
p = Memo1->Lines->Text.Pos("id=20");
if (p != 0)
{
id10 = Memo1->Lines->Text.SubString(p + 1, Memo1->Lines->Text.Length());
ShowMessage(id10);
}
......
Unfortunately, its always failed. Because I am new to c++ and the sort but I don't know what to do with all of it. I am sorry.
So I need your help to handle this situation.
Thank you very much.
Calling .Pos() from the text property is getting the index for the position of the found string within the entire text of the memo control. Here is a short example of how to read each line from a TMemo in C++ Builder:
AnsiString id10;
for (int i = 0; i < Memo1->Lines->Count; i++)
{
AnsiString line = Memo1->Lines->Strings[i];
if (line.Pos("id=10") > 0)
{
id10 = line;
ShowMessage(id10);
}
}
Using the above snippet, you should be able to deduce how to make it do what you want.

Gtk::TextView with constant string

I am using Gtkmm 3+ and What I am trying to do is have the text buffer have the constant string "> " even if the user tries to delete it. In addition when the user pressed return it will automatically be there again. Basically have a constant string like a terminal does.
The only way I can think about about accomplishing this would be to connect to the delete and backspace signals so the user cannot delete the string. But, is there a better way?
so far this is the only way I can think of:
//in constructor
txt_view_i_.signal_event().connect(sigc::mem_fun(*this, &MainWindow::inputEvent));
//function
bool MainWindow::inputEvent(GdkEvent* event)
{
if((event->key.keyval == GDK_KEY_BackSpace || event->key.keyval == GDK_KEY_Delete) && buffer_input_->get_char_count() < 3)
return true;
return false;
}
But doesn't work perfectly, because if you type in more then 3 characters then go to the beginning of the line you can delete the constant string.
Another way I just thought about was to add a label to the TextView widget. I did that but, the user could still delete it. Here is the code for that:
Gtk::TextBuffer::iterator it = buffer_input_->get_iter_at_line(1);
Glib::RefPtr<Gtk::TextChildAnchor> refAnchor = buffer_input_->create_child_anchor(it);
Gtk::Label* lbl = Gtk::manage(new Gtk::Label("> "));
txt_view_i_.add_child_at_anchor(*lbl, refAnchor);
This is very similar, but not quite identical, to the question I answered here: You can create a GtkTextTag that makes its contents uneditable, and apply it from the beginning of the buffer up to and including the "> " prompt.
Then when you receive input, append your output to the buffer and then append a new prompt on the next line, and re-apply the tag to make the whole thing uneditable.
The links in the linked answer show some C code where this is done, even including a prompt. It's not Gtkmm or C++, but it should serve as an illustration.
Here is the code I used to solve it:
Glib::RefPtr<Gtk::TextBuffer::Tag> tag = Gtk::TextBuffer::Tag::create();
tag->property_editable() = false;
Glib::RefPtr<Gtk::TextBuffer::TagTable> tag_table = Gtk::TextBuffer::TagTable::create();
tag_table->add(tag);
buffer_input_ = Gtk::TextBuffer::create(tag_table);
txt_view_i_.set_buffer(buffer_input_);
scroll_win_i_.add(txt_view_i_);
Gtk::TextBuffer::iterator buffer_it_ = buffer_input_->begin();
buffer_input_->insert_with_tag(buffer_it_, "> ", tag);
Here is how I made it so that the user cannot edit before the constant string:
//connect to the mark set signal
buffer_input_->signal_mark_set().connect(sigc::mem_fun(*this, &MainWindow::setMark));
//make the box uneditable
void MainWindow::setMark(const Gtk::TextBuffer::iterator& it, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark)
{
if(it.get_offset() < 2)
txt_view_i_.set_editable(false);
else
txt_view_i_.set_editable(true);
}
Hopefully someone will find this useful.

Reading a random line from a txt file, then trim it in QT SDK

I'd like to ask your help with my little school project.
The task is to determine a person's gender (using 2 radio buttons) and then pick a random Japanese family name and male/female middlename. And there's the rest of the task, but it's nothing compared to this part :(
Thing is, i have managed to make the 3 .txt files (familynames.txt, malemiddlenames.txt and femalemiddlenames.txt) look like the following:
1,Akiro
2,Sakura
3,etc...
What i'd like to do is create a random number, and read the lines until it arrives to the line with the same number as my random number, then cut the number and the comma off, and display the name on the corresponding label. So far this is what i've got:
void MainWindow::famname()
{
QString familyname;
int famrand =qrand() % 76;
ui->label_2->setText(QString::number(famrand));
int i = 1;
QFile famfile("C:\Users\Ryseth\gyakorlas\_familynames.txt");
QTextStream in(&famfile);
if(famfile.open(QIODevice::ReadOnly)){
while (!in.atEnd()) {
QString line = in.readLine();
i++;
if(i==famrand){
QStringList line2 =line.split(',');
familyname = line2.at(0);
ui->label_2->setText(QString::number(famrand)+" "+QString::number(i));
ui->FamilyLabel->setText(familyname);
}//IF
}//WHILE
}//IF
famfile.close();
}//NGEN
If any of you could think of some sort of solution or if you have ANY suggestions, please don't hasitate to share it with me :D
Thank you, and have a nice day/night : Ruben
I think with Boost::Spirit::Qi you could parse your file into a std::vector< std::string > and do your operation with simple c++ methods.
But to help you with your Qt-Solution:
You never check if int famrand =qrand() % 76; produces a legal number, are there enough entries in your text file ...
int i = 1; This integer is unnecessary, the number is within the text file ...
My solution (untested):
while (!in.atEnd()) {
QString line = in.readLine();
QStringList list = line.split(",", QString::SkipEmptyParts);
bool ok;
int idx = list.at(0).toInt(&ok);
if (ok && idx == famrand) {
familyname = list.at(1).trimmed();
// ... do with your ui whatever you want
}//IF
}//WHILE
Keep in mind you have to do error handling if the conversion of string to int fails and/or the accessors of list throw (list.at(xx))
The positive thing is, you dont need an ordered text file!
I don't really understand what you are managing to do but at this line I guess you are getting the number, not the familyname.
familyname = line2.at(0); // number
familyname = line2.at(1); // family name