check/determine if QString contains html - c++

Since I wasn't able to find a suitable solution here, I wanted to Q&A this question:
Is there a way to determine if a QString is made of html, i.e. is rich-text, (or at least, contains html)?
This may be the case for unknown/QVariant calls to setData of data editors in the table/view model.

A solution can be to use Qt::mightBeRichText for QString:
#include <QTextDocument>
QString ensurePlainText(const QString& text)
{
QString out;
if (Qt::mightBeRichText(text))
{
// is html -> convert to plain text
QTextDocument text;
text.setHtml(value.toString());
out = text.toPlainText();
}
else
{
out = text;
}
return out;
}
It is important to note that the presented method uses a heuristic. It may fail to detect html or falsely detect html in a non-html text. The former may return html tags in the string. The latter would, for instance, strip newline characters from the text.

Related

How to use QStringList to store multiple strings

I'm using QT framework to create an employee interface which stores information of an employee (ID, name, surname, skills, ecc..)
In the widget interface I've used a lineEdit for everything since I need to store string types of information, the only exception of parameter skills that should be a list of strings
In my mainwindow.cpp however I should store a list of different skills, not just one, and that's get me the error when I use my function .addEmployee() because it wants a list of skills, not one string.
I should use QStringList skills but how can I tell the user is inserting different skills and store them as a list?
void MainWindow::on_add_pushButton_clicked()
{
QString id = ui->id_lineEdit->text();
QString nome = ui->name_lineEdit->text();
QString cognome = ui->surname_lineEdit->text();
QString skills = ui->skills_lineEdit->text();
// should be QStringList skills but how can I tell the user is inserting different skills?
manage.addEmployee(id.toStdString(), name.toStdString(), surname.toStdString(), skills.toStdString());
// manage refers to the class manageEmployee.h I already implemented to use function addEmployee(id, name, surname, {skills});
}
The question is a bit unrefined, but I will try my best to answer based on what I could follow.
void MainWindow::on_add_pushButton_clicked()
{
QString id = ui->id_lineEdit->text();
QString name = ui->name_lineEdit->text();
QString surname = ui->surname_lineEdit->text();
QString skills = ui->skills_lineEdit->text();
QStringList skillsList = skills.split(","); // or ";", etc.
std::list<std::string> skillsStdList;
for (const QString& qstr : skillsList) {
skillsStdList.push_back(qstr.toStdString());
}
manage.addEmployee(id.toStdString(), name.toStdString(), surname.toStdString(), skillsStdList);
}
So if you want the user to input list of skills, you wanna be splitting them using a delimiter as well.
Then you can pass the resulting QStringList to .addEmployee assuming it takes a fourth argument std::list<std::string>
By using a delimiter, you allow the user to enter multiple skills as a single input, which simplifies the interface. However, you should make sure to handle any errors that can arise from invalid input, such as an empty or malformed input string.
Edit: Code updated in mind with QList<Qstring> not supporting toStdString method

How Do I Save and Load Data Using QSettings?

I'm a complete beginner to QT5, I searched YouTube for any QSettings tutorials and only found 2 of them, both in Spanish.
I'm trying to store simple text from a textEdit and then load it on save/load button click. So far I have not been able to accomplish this. Here's my code so far, no errors, it just doesn't work.
Widget.cpp
void Widget::saveText(QString key, QString text)
{
QSettings settings("App", "BillReminder");
settings.beginGroup("Text");
settings.setValue(key + "t", text);
settings.endGroup();
}
QString Widget::loadText(QString key)
{
QSettings settings("App", "BillReminder");
settings.beginGroup("Text");
settings.value(key + "t", text).toString();
settings.endGroup();
return QString(text);
}
void Widget::on_saveButton_clicked()
{
saveText("textEdit", text);
}
void Widget::on_loadButton_clicked()
{
QString text1 = loadText(text);
ui->textEdit->setText(text1);
}
widget.h - class Widget : public QWidget
private:
Ui::Widget *ui;
QString text;
void saveText(QString key, QString text);
QString loadText(QString key);
void SetText(QString key);
The problem is in your loadText() method. QSettings::value() is a function that returns a value retrieved from the QSettings storage. The second parameter is only a default value, that would be returned in case your settings storage doesn't contain the requested key.
QString Widget::loadText(QString key)
{
QSettings settings("App", "BillReminder");
settings.beginGroup("Text");
QString theValue = settings.value(key + "t", text).toString();
settings.endGroup();
return theValue;
}
This code example contains many issues.
don't shadow variable names (e.g. "text" is method argument and member variable); use e.g. underscore to indicate member variables
above answer about reading values as return value solve one issue as well
on_saveButton is using as a settings key a "textEdit" string but on_loadButton is used wrong "text" member variable string as key -> you want use the same string to load stored variable i.e. you are reading something else just now.
you are saving member variable "text" that is not initialised in your example i.e. it may be empty; and later you are setting UI text edit with stored settings (empty string in your example?)
Please go through QSettings Qt documentation for working example.

What is the way to replace jsoncpp FastWriter with Streambuilder?

I have a jsoncpp value that I want to serialize. The most straightforward way is like this:
Json::Value val; // population is left as an exercise for the reader
std::string str = Json::FastWriter().write(val);
The problem is that FastWriter is deprecated, and I can't tolerate compiler warnings. According to the less-than-intuitive documentation, I'm supposed to use StreamWriterBuilder instead:
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = "";
std::unique_ptr<Json::StreamWriter> writer( builder.newStreamWriter() );
std::ostringstream os;
writer->write(val, &os);
std::string str = os.str();
Surely this can't be "better"? I'm assuming the fault lies with me and there is a straightforward way to perform minimal serialization (without extraneous whitespace).
This shows a slightly more compact form (although it appears to simply wrap the above in a function call).
Json::StreamWriterBuilder builder;
builder["indentation"] = ""; // assume default for comments is None
std::string str = Json::writeString(builder, val);
Is that the right way now?
You're doing it the right way.
The second example is just a shorthand for the first — in both instances, a builder dynamically allocates a writer, the writer is used and then the writer is freed.
Why the older FastWriter implementation was deprecated I could not say. Personally I miss it a bit. But a factory is more flexible and permits different implementations slotting into the same suite of functions. You'd have to ask the JsonCpp developers whether that was the core of their decision.
If you always want to write with the same settings (e.g. your ["indentation"] = ""), you can supply a Json::Value representing the settings to Json::StreamWriterBuilder::setDefault, then writing strings doesn't need to name Json::StreamWriterBuilder again (but does default construct one)
Startup
Json::Value streamWriterSettings{R"({ "indentation" : "" })"};
Json::StreamWriterBuilder::setDefault(&streamWriterSettings);
Usage
std::string str = Json::writeString({}, val);

QValidator prevents intermediate typing

I have a QLineEdit that accepts a string that will be evaluated at a javascript expression like "[0,3]" and connected to fire changes using editingFinished(). I added a validator so the user couldn't leave the input with a bad expression, but apparently I'm misunderstanding how the validator works, because if I return QValidator::Invalid, when the expression wouldn't be valid, user's can't type any mistakes (character won't disappear on backspace). For example, temporarily changing "[0,3]" to "[0,]" to fill in another number.
I've tried changing the validator to return to QValidator::Intermediate on bad expressions thinking that would be a happy medium letter users alter text, but setting bad text back to its previous value on unfocused or return, but that seems to let user's put in anything that want. For example, they can type in "[0," and click on something else and the input still has "[0," in it as opposed to jumping back to the way it was. Am I misunderstanding how the intermediate type works?
QValidator::Invalid 0 The string is clearly invalid.
QValidator::Intermediate 1 The string is a plausible intermediate value.
QValidator::Acceptable 2 The string is acceptable as a final result;
i.e. it is valid.
Here is my current validator, which I just put on a QLineEdit:
class PointFValidator : public QValidator
{
QValidator::State validate(QString &input, int &position) const;
void fixup(QString &input) const;
};
QValidator::State PointFValidator::validate(QString &input, int &position) const
{
try {
evalPointF(input);
} catch (std::exception &e) {
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
void PointFValidator::fixup(QString &input) const
{
}
And this is what actually tests the string to see if it's formatted correctly
QPointF evalPointF(QString s)
{
//initGuile();
QScriptEngine engine;
QString program = "function frame() { return 9; }\n\n" + s;
QScriptValue value = engine.evaluate(program);
QStringList pair = value.toString().split(",");
if (pair.length() < 2)
throw std::runtime_error("invalid pointf string");
return QPointF(pair[0].toFloat(), pair[1].toFloat());
}
Is the QValidator not what I need? Is it only for typing prevention? Do I need to listen to the change event, validate it myself, and set it back if it's not valid?
So trying to compile your code didn't work for me. The constness of the virtual functions in QValidator make it pretty much impossible to get your example code to compile.
So I would (like you mentioned at the end of your question) go and set up a signal to respond to the content changes of your QLineEdit, evaluate it, and then put the output somewhere helpful.
But based on the kinds of things you put in your evalPointF function, it looks like you are just writing an IDE for javascript.
Why not use the pattern that most IDE's already have of putting issues in another window, and using font's and formatting to modify the text, instead of actually changing the text?
Hope that helps.

How to paste xml to C++ (Tinyxml)

I'm currently working on a project in C++ where I need to read some things from a xml file, I've figured out that tinyxml seams to be the way to go, but I still don't know exactly how to do.
Also my xml file is a little tricky, because it looks a little different for every user that needs to use this.
The xml file I need to read looks like this
<?xml version="1.0" encoding="utf-8"?>
<cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xmlns:d="http://www.kuju.com/TnT/2003/Delta" d:version="1.0">
<cCareerModel d:id="154964152">
<ScenarioCareer>
<cScenarioCareer d:id="237116344">
<IsCompleted d:type="cDeltaString">CompletedSuccessfully</IsCompleted>
<BestScore d:type="sInt32">0</BestScore>
<LastScore d:type="sInt32">0</LastScore>
<ID>
<cGUID>
<UUID>
<e d:type="sUInt64">5034713268864262327</e>
<e d:type="sUInt64">2399721711294842250</e>
</UUID>
<DevString d:type="cDeltaString">0099a0b7-e50b-45de-8a85-85a12e864d21</DevString>
</cGUID>
</ID>
</cScenarioCareer>
</ScenarioCareer>
<MD5 d:type="cDeltaString"></MD5>
</cCareerModel>
</cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
Now the goal of this program is to be able to insert some string (via. a variable) and serch for the corresponding "cScenarioCarrer d:id" and read the "IsComplete" and the "BestScore".
Those strings later need to be worked with in my program, but that I can handle.
My questions here are
A. How do I go by searching for a specific "cScenarioCareer" ID
B. How do I paste the "IsComplete" and "BestScore" into some variables in my program.
Note: The xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx string is unique for every user, so keep in mind it can be anything.
If anyone out there would like to help me, I'd be very graceful, thank you.
PS. I'd like to have some kind of understanding for what I'm doing here, all though "paste this code into your program" answers are acceptable, I think it would be much better if you can tell me how and why it works.
Since you're doing this in C++ I'll make this example using the ticpp interface to
TinyXml that available at ticpp.googlecode.com.
Assumptions:
A given xml file will contain one <cloud> tag and multiple
<cCareerModel> tags.
Each <cCareerModel> contains a single <ScenarioCareer> tag which in turn contains a single <cScenarioCareer> tag
You've parsed the xml file into a TiXmlDocument called xmlDoc
You don't need to examine the data type attributes
You don't mind using exceptions
I'll also assume that you have a context variable somewhere containing a pointer to the
<cloud> tag, like so:
ticpp::Element* cloud = xmlDoc.FirstChildElement("cloud");
Here's a function that will locate the ticpp::Element for the cScenarioCareer with
the given ID.
ticpp::Element* findScenarioCareer(const std::string& careerId)
{
try
{
// Declare an iterator to access all of the cCareerModel tags and construct an
// end iterator to terminate the loop
ticpp::Iterator<ticpp::Element> careerModel;
const ticpp::Iterator<ticpp::Element> modelEnd = careerModel.end();
// Loop over the careerModel tags
for (careerModel = cloud->FirstChildElement() ; careerModel != modelEnd ;
++careerModel)
{
// Construct loop controls to access careers
ticpp::Iterator<ticpp::Element> career;
const ticpp::Iterator<ticpp::ELement> careerEnd = career.end();
// Loop over careers
for (career = careerModel->FirstChildElement("ScenarioCareer").FirstChildElement() ;
career != careerEnd ; ++career)
{
// If the the d:id attribute value matches then we're done
if (career->GetAttributeOrDefault("d:id", "") == careerId)
return career;
}
}
}
catch (const ticpp::Exception&)
{
}
return 0;
}
Then to get at the information you want you'd do something like:
std::string careerId = "237116344";
std::string completion;
std::string score;
ticpp::Element* career = findScenarioCareer(careerId);
if (career)
{
try
{
completion = career->FirstChildElement("IsCompleted")->GetText();
score = career->FirstChildElement("BestScore")->GetText();
}
catch (const ticpp::Exception&)
{
// Handle missing element condition
}
}
else
{
// Not found
}
Naturally I haven't compiled or tested any of this, but it should give you the idea.