QWebEngine: How to get attribute value? - c++

I want to rewrite my project writen in QWebView to QWebEngineView, but I don't know how to get the attribute value and assign it to a C++ variable. I think that I should use QWebEnginePage::runJavaScript function. But I don't know how.
For example, when I try to display value of element in console, I did this:
web->page()->runJavaScript("document.getElementById(\"login\").getAttribute(\"va‌​lue\")", []
(QVariant result)->void {
qDebug()<<result.toString();
});
But It display nothing.

I believe you can get the value of your element by connecting a slot to the loadFinished signal of QWebView. The code should look something like below.
void WebView::onPageLoadFinished(bool status)
{
if(status)
{
QVariant value;
QWebFrame* frame = this->page()->currentFrame();
if (frame!=NULL)
{
QWebElement element = frame->findFirstElement("input[id=login]");
value = element.attribute("value");
}
//In your case you can set a class variable instead of local variable value
}
}
This method is clean and you can even add a url check right in the beginning of the SLOT and continue further only if the signal came from the url you are interested in.
EDIT: I just re-read your question and found that you want to implement it with QWebEngine. I am not sure if it can be done there. According to this page, you can't access the internal elements. I would love to know if there is a work around.

runJavaScript will definitely do it: Here is a functional example using pure Javascript to interact with a field named "email":
// This will set the value
this->_view->page()->runJavaScript(
"document.querySelector('input[name=\"email\"]').value = \"JKLJKLJKL\";"
);
// This will retrieve the value
this->_view->page()->runJavaScript(
"document.querySelector('input[name=\"email\"]').value;",
[](const QVariant &result){
qDebug() << "Value is: " << result.toString() << endl;
}
);
Remember that the scope of the QVariant is limited to the callback, if you need to use the returned value outside of the function, you will want to pass a variable that will survive until callback is called in the [].

There is the direct access to DOM attributes/properties in JS:
document.getElementById("login").va‌​lue
Also I would recommend to use jQuery (take a look at fancybrowser example). Code for set/get value of login input is:
if( qt.jQuery( 'input#login' ).length ) // can be omitted if you are sure that DOM element is ready
{
qt.jQuery( 'input#login' ).attr( 'value', currentUserName ); //-- set value
return qt.jQuery( 'input#login' ).attr( 'value' ); //-- get
}

This works fine.
_view->page()->runJavaScript("document.querySelector(input[name=\"email\"]).value");
Just add your callback as second parameter. I hope this helps you.

This works fine.
_view->page()->runJavaScript("document.querySelector(input[name=\"email\"]).value");
Just add your callback as second parameter to get the value. I hope this helps you.

Related

C++ proto2 "expression is not assignable" error

I am trying to figure out how to assign a message field in protobuf2 in C++. Here is a small snippet of the code.
message Sub {
optional double x = 1 [[default = 46.0];
}
message Master {
optional Sub sub_message;
}
Now when I try to initialize a Master message, I got the following error:
Master msg;
msg.mutable_sub_message() = new Sub();
error: expression is not assignable
However, the following code works, and the sub_message is set to default values:
Master msg;
msg.set_sub_message(new Sub());
Can anyone kindly explain why mutable_sub_message() can not be used for assignment?
msg.mutable_sub_message() returns a pointer to the field, i.e. a Sub*. The idea is that you use that pointer to manipulate the field as you need.
Assigning a different pointer to it wouldn't change the value inside the class, it would at most change the temporary pointer that was returned, which doesn't make sense. I guess it could be made to work if mutable_sub_message returned something like a Sub*& (not even sure that syntax is right), but that's not how the library was written.
In a more practical note, calling mutable_sub_message will initialize the subfield, you don't need to do that explicitly. That means you'd usually set a nested field using
Master msg;
msg.mutable_sub_message()->set_x(4.0);
Also, it's always safe to call getters even if a field isn't set, in that case they will always return a default instance. In other words:
double use_field(const Master& msg) {
// This is always safe, and will return the default even if
// sub_message isn't set.
return msg.sub_message().x();
}

What does it mean to "Implement an empty logic" for a function in ue4?

I'm following a tutorial for setting up a character interaction, and part of it says to make a header file with the following code:
public:
/*This property will be used in order to bind our subtitles
Binding will make sure to notify the UI if the content of the following
variable change.*/
UPROPERTY(BlueprintReadOnly)
FString SubtitleToDisplay;
/*Updates the displayed subtitles based on the given array*/
UFUNCTION(BlueprintCallable, Category = DialogSystem)
void UpdateSubtitles(TArray<FSubtitle> Subtitles);
/*This array will populate our buttons from within the show function*/
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
TArray<FString> Questions;
/*Adds the widget to our viewport and populates the buttons with the given questions*/
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = DialogSystem)
void Show();
Then, it tells me to "Implement an empty logic for the UpdateSubtitles function for now." I have no idea what this means, and considering that UpdateSubtitles was the one thing to give me an error when I compiled this code, it's probably something important. Does anyone know what this terminology refers to?
It means to just leave the contents of the function blank or return an empty result such as:
FString AMyCharacter::GetNickname()
{
return "";
}
in the case where the return type isn't void.
I figured it out thanks to your comment! However it was a little different than what you described, it was actually:
void UDialogUI::UpdateSubtitles(TArray<FSubtitle> Subtitles)
{}
And only this line; adding a definition for Show() as suggested actually threw an error.

Updating data on a XML DOM c++ using tinyxml-2

I was wondering how could I update the data on the DOM for a certain attribute? I've searched but I couldn't find anything. Basically, I have an attribute called Hour(for example it's "11:03") and I want the text from that specific attribute to be changed to something like "11:04" or any other different text.
if( strcmp(Code1,Code2) == 0 )
{
strcpy(New,NewHour);
Element->FindAttribute("Hour")->SetAttribute(New); // here I want it to be changed in the DOM but I dont know how to do it
}
Later edit: This is what I've tried, but it's telling me FindAttribute() is private..
It is true that you can use SetAttribute which accepts the attribute name and value as parameters.
However, TinyXml2 does have a methodology for using FindAttribute because I have this code in my application:
// We need to get the assistant
const XMLAttribute *pAttrAssistant = const_cast<const XMLElement*>(pStudent)->FindAttribute("Assistant");
if (pAttrAssistant != nullptr)
{
LPCTSTR szAssistant = CA2CT(pAttrAssistant->Value(), CP_UTF8);
SetStudentInfo(eSchool, eAssign, strStudent, szAssistant, iStudyPoint);
}
else
{
// TODO: Throw exception if Assistant attribute missing
}
As you can see, I use the FindAttribute method and I have no compilation errors. If you look closely you will see that I am using const and that is the key.
The class exposes two methods:
One of them is set to private as you have already found out. But the const overload is set as public:

How to change QJsonObject value in a QJson hierarchy without using copies?

I am currently using Qt5.0 with the core QJson library to handle some data for the program I am developing.
To set the scene for this question I will provide you with some JSON data that illustrates my problem:
{
"CLOCKS": [
{
"ID": "clk",
"MAX": 2e+08,
"MIN": 1e+07,
"VALUE": "no_clock"
},
{
"ID": "memclk",
"MAX": 2e+08,
"MIN": 1e+07,
"VALUE": "memclk"
}
]
}
Here we have a parent QJsonObject containing a single key 'CLOCKS'. The value for this key is a QJsonArray of QJsonObjects that contain a number of key/value pairs that contain my data.
If I wanted to retrieve the QJsonObject with id 'clk' I am currently using code like this:
// imagine m_data is my parent QJsonObject
QJsonArray clocks = m_data["CLOCKS"].toArray();
foreach (const QJsonValue & value, clocks) {
QJsonObject obj = value.toObject();
if (obj["ID"].toString() == "clk") {
return obj;
}
}
This works fine and the library has been great so far. However, I have started running into issues recently when I want to obtain a QJsonObject reference for modification instead of a copy.
So my question is, given the sample data provided how do I obtain a QJsonObject reference in order to modify the key/value pairs in the desired clock data object. The problem manifests itself, IMO due to the fact that you can obtain QJsonValueRefs, which are references to the value entries... but to actually access the data inside this (if the value is another array/object) you must convert using the toArray(), toObject() functions etc. This functions only return copies and not references creating a barrier to iterating down the object hierarchy with references.
The only way I have come up with so far to get around this is to create a copy of the entire "CLOCKS" QJsonArray, find the object I want then delete it and reinsert it with the changed data... and finally assign the entire array back to the "CLOCKS" key in the parent object. This seems cumbersome enough to me to me that I feel like I am doing something wrong and there must be a better way.
To support this here is what my code looks like so far... just to change the "VALUE" for one of the clock QJsonObjects:
QJsonArray resets = m_data.value(TAG_RESETS).toArray();
// get a copy of the QJsonObject
QJsonObject obj;
foreach (const QJsonValue & value, resets) {
QJsonObject o = value.toObject();
if (o.value(TAG_ID).toString() == id) {
obj = o;
break;
}
}
// modify the object
obj[TAG_VALUE] = "NEW VALUE";
// erase the old instance of the object
for (auto it = resets.begin(); it != resets.end(); ++it) {
QJsonObject obj = (*it).toObject();
if (obj.value(TAG_ID).toString() == id) {
resets.erase(it);
// assign the array copy which has the object deleted
m_data[TAG_RESETS] = resets;
break;
}
}
// add the modified QJsonObject
resets.append(obj);
// replace the original array with the array containing our modified object
m_data[TAG_RESETS] = resets;
I know this could be shortened a little bit but it still seems like there must be a better way to change a single value in a QJson object hierarchy without going to all this effort!!!
After wasting three hours of my life I can confirm that as of today this is still impossible with Qt 5.4. You can modify JSON objects, but not nested JSON objects.
The problem is that the code such as:
json["aa"].toObject()["bb"] = 123;
essentially means the following:
QJsonObject temp = json["aa"].toObject();
temp["bb"] = 123;
and since temp is not a reference but object (and toObject() doesn't return a reference), the assignment is compiled but then discarded.
Essentially it breaks down to the fact that it is impossible to obtain the reference to an object you just created, meaning you cannot create them from left to right, i.e. aa["bb"] -> aa["bb"]["cc"] etc - you cannot obtain reference to aa["bb"], only a copy of its value.
What IS possible though is to recreate the JSON with a new value added, as described here: https://forum.qt.io/topic/25096/modify-nested-qjsonvalue/4 - note that this keeps recreating the object each time it is called, and is essentially memory usage disaster, but this is all Qt currently allows.
According to information from Qt developer who actually wrote QJson in Qt5 -
What's currently included in Qt is a 'read-only' implementation to provide parsing facilities. He has an intention to extend design with 'references' support in future, but it's not yet done.
I have had a similar problem for a couple of days and I have managed to find a workaround which works for me and I thought I should share it here.
You can navigate to the object whose key-value you wish to update. Then use the "remove" method to delete the key-value pair and then use the "insert" method to insert it again with the new value.
This might ruin the order of key-value pairs in your object but since you will anyways access by a key, it should not be a problem.
The in-place changing of values is not supported as I found out the hard way :)

How do I reference successive components (button1, button2, etc.)?

I need to get the number after the button to increment in a for loop. For example, button1 becomes button2, etc. I have tried appending a variable which increments but C++ Builder gives an error saying "Button is not a member of TMain." Is there any way to achieve the end goal or get around this?
You can't construct new identifiers from others at run time. The compiler is correct that Button really isn't a member of your TMain class.
Instead, build the string name of the component you want, and then call your form's FindComponent method to get the component with that name.
for (int i = 1; i <= 2; ++i) {
std::string name = "Button" + IntToStr(i);
TButton* button = dynamic_cast<TButton*>(this->FindComponent(name));
}
That requires that the buttons' Name properties be set accordingly.
Another solution is to forego the component names and put your objects in a proper container, like a vector. For example, you can override the Loaded method (which is where you can be sure all your form's components have been created) and fill a vector there:
void TMain::Loaded() {
TForm::Loaded();
this->m_buttons.push_back(Button1);
this->m_buttons.push_back(Button2);
}
Now when you want to iterate over your buttons, you just iterate over the vector instead:
for (std::vector<TButton*>::const_iterator it = m_buttons.begin();
it != m_buttons.end();
++it) {
// ...
}