How to get the QWebFrame for an iframe/frame QWebElement? - qt

I have simple Qt GUI application which uses QtWebkit. I am loading complex page with a lot of nested IFRAME-tags. And I want to traverse complete DOM tree (like Chrome browser does in debug-panel) including content of iframes.
My code is:
QWebElement doc = ui->webView->page()->mainFrame()->documentElement();
QWebElement iframe = doc.findFirst("iframe[id=someid]");
QWebFrame *webFrame = ....? // how to get the QWebFrame for an iframe/frame QWebElement?
Note:
I can traverse over all frames (including nested):
void MainWindow::renderFramesTree(QWebFrame *frame, int indent)
{
QString s;
s.fill(' ', indent * 4);
ui->textLog->appendPlainText(s + " " + frame->frameName());
foreach (QWebFrame *child, frame->childFrames())
renderFramesTree(child, indent + 1);
}
But my question is not about that. I need to get corresponding QWebFrame* of iframe-QWebElement.
Thanx!

Each QWebFrame has QList<QWebFrame *> QWebFrame::childFrames () const method. Each frame has also QString QWebFrame::frameName () const. Combining both may let you find what you need.
QWebFrame * frameImLookingFor = NULL;
foreach(QWebFrame * frame, ui->webView->page()->mainFrame()->childFrames())
{
if (frame->frameName() == QLatin1String("appFrame"))
{
frameImLookingFor = frame;
break;
}
}
if (frameImLookingFor)
{
// do what you need
}

run selector on current frame
if it returns something that's good we found that our webframe is on the current frame, lets call this element X :
now get all frame elements using selector "iframe,frame" from current page
loop trough all those elements and try to match them with X frame
this way you'll be able to find exact INDEX of the X frame in document
finally you are now able to focus on child frame with that INDEX
else this means that selector is not found in this frame
loop trough all child frames and repeat the whole process for each child frame

Related

NewTek NDI (SDK v5) with Qt6.3: How to display NDI video frames on the GUI?

I have integrated the NDI SDK from NewTek in the current version 5 into my Qt6.3 widget project.
I copied and included the required DLLs and header files from the NDI SDK installation directory into my project.
To test my build environment I tried to compile a simple test program based on the example from "..\NDI 5 SDK\Examples\C++\NDIlib_Recv".
That was also successful.
I was therefore able to receive or access data from my NDI source.
There is therefore a valid frame in the video_frame of the type NDIlib_video_frame_v2_t. Within the structure I can also query correct data of the frame such as the size (.xres and .yres).
The pointer p_data points to the actual data.
So far so good.
Of course, I now want to display this frame on the Qt6 GUI. In other words, the only thing missing now is the conversion into an appropriate format so that I can display the frame with QImage, QPixmap, QLabel, etc.
But how?
So far I've tried calls like this:
curFrame = QImage(video_frame.p_data, video_frame.xres, video_frame.yres, QImage::Format::Format_RGB888);
curFrame.save("out.jpg");
I'm not sure if the format is correct either.
Here's a closer look at the mentioned frame structure within the Qt debug session:
my NDI video frame in the Qt Debug session, after receiving
Within "video_frame" you can see the specification video_type_UYVY.
This may really be the format as it appears at the source!?
Fine, but how do I get this converted now?
Many thanks and best regards
You mean something like this? :)
https://github.com/NightVsKnight/QtNdiMonitorCapture
Specifically:
https://github.com/NightVsKnight/QtNdiMonitorCapture/blob/main/lib/ndireceiverworker.cpp
Assuming you connect using NDIlib_recv_color_format_best:
NDIlib_recv_create_v3_t recv_desc;
recv_desc.p_ndi_recv_name = "QtNdiMonitorCapture";
recv_desc.source_to_connect_to = ...;
recv_desc.color_format = NDIlib_recv_color_format_best;
recv_desc.bandwidth = NDIlib_recv_bandwidth_highest;
recv_desc.allow_video_fields = true;
pNdiRecv = NDIlib_recv_create_v3(&recv_desc);
Then when you receive a NDIlib_video_frame_v2_t:
void NdiReceiverWorker::processVideo(
NDIlib_video_frame_v2_t *pNdiVideoFrame,
QList<QVideoSink*> *videoSinks)
{
auto ndiWidth = pNdiVideoFrame->xres;
auto ndiHeight = pNdiVideoFrame->yres;
auto ndiLineStrideInBytes = pNdiVideoFrame->line_stride_in_bytes;
auto ndiPixelFormat = pNdiVideoFrame->FourCC;
auto pixelFormat = NdiWrapper::ndiPixelFormatToPixelFormat(ndiPixelFormat);
if (pixelFormat == QVideoFrameFormat::PixelFormat::Format_Invalid)
{
qDebug().nospace() << "Unsupported pNdiVideoFrame->FourCC " << NdiWrapper::ndiFourCCToString(ndiPixelFormat) << "; return;";
return;
}
QSize videoFrameSize(ndiWidth, ndiHeight);
QVideoFrameFormat videoFrameFormat(videoFrameSize, pixelFormat);
QVideoFrame videoFrame(videoFrameFormat);
if (!videoFrame.map(QVideoFrame::WriteOnly))
{
qWarning() << "videoFrame.map(QVideoFrame::WriteOnly) failed; return;";
return;
}
auto pDstY = videoFrame.bits(0);
auto pSrcY = pNdiVideoFrame->p_data;
auto pDstUV = videoFrame.bits(1);
auto pSrcUV = pSrcY + (ndiLineStrideInBytes * ndiHeight);
for (int line = 0; line < ndiHeight; ++line)
{
memcpy(pDstY, pSrcY, ndiLineStrideInBytes);
pDstY += ndiLineStrideInBytes;
pSrcY += ndiLineStrideInBytes;
if (pDstUV)
{
// For now QVideoFrameFormat/QVideoFrame does not support P216. :(
// I have started the conversation to have it added, but that may take awhile. :(
// Until then, copying only every other UV line is a cheap way to downsample P216's 4:2:2 to P016's 4:2:0 chroma sampling.
// There are still a few visible artifacts on the screen, but it is passable.
if (line % 2)
{
memcpy(pDstUV, pSrcUV, ndiLineStrideInBytes);
pDstUV += ndiLineStrideInBytes;
}
pSrcUV += ndiLineStrideInBytes;
}
}
videoFrame.unmap();
foreach(QVideoSink *videoSink, *videoSinks)
{
videoSink->setVideoFrame(videoFrame);
}
}
QVideoFrameFormat::PixelFormat NdiWrapper::ndiPixelFormatToPixelFormat(enum NDIlib_FourCC_video_type_e ndiFourCC)
{
switch(ndiFourCC)
{
case NDIlib_FourCC_video_type_UYVY:
return QVideoFrameFormat::PixelFormat::Format_UYVY;
case NDIlib_FourCC_video_type_UYVA:
return QVideoFrameFormat::PixelFormat::Format_UYVY;
break;
// Result when requesting NDIlib_recv_color_format_best
case NDIlib_FourCC_video_type_P216:
return QVideoFrameFormat::PixelFormat::Format_P016;
//case NDIlib_FourCC_video_type_PA16:
// return QVideoFrameFormat::PixelFormat::?;
case NDIlib_FourCC_video_type_YV12:
return QVideoFrameFormat::PixelFormat::Format_YV12;
//case NDIlib_FourCC_video_type_I420:
// return QVideoFrameFormat::PixelFormat::?
case NDIlib_FourCC_video_type_NV12:
return QVideoFrameFormat::PixelFormat::Format_NV12;
case NDIlib_FourCC_video_type_BGRA:
return QVideoFrameFormat::PixelFormat::Format_BGRA8888;
case NDIlib_FourCC_video_type_BGRX:
return QVideoFrameFormat::PixelFormat::Format_BGRX8888;
case NDIlib_FourCC_video_type_RGBA:
return QVideoFrameFormat::PixelFormat::Format_RGBA8888;
case NDIlib_FourCC_video_type_RGBX:
return QVideoFrameFormat::PixelFormat::Format_RGBX8888;
default:
return QVideoFrameFormat::PixelFormat::Format_Invalid;
}
}

IMPORTDATA not grabbing live data from XML

I'm using Google Sheet's IMPORTDATA function to grab information from an XML file that is pulling from an API but the information I pull into the sheet isn't up to date.
How can I modify my sheet to pull in up-to-date data?
Compare the sheet: https://docs.google.com/spreadsheets/d/1W0Bt5z-Tky-tNhG_JtfE4FfjTRgQNRu_eQu2qVhQ-_E/edit?usp=sharing (LiveScores sheet)
To the XML: https://www67.myfantasyleague.com/2019/export?TYPE=liveScoring&L=64741&APIKEY=&W=14&DETAILS=1&JSON=0
Observe franchise id="0015" in both sets of data.
The sheet states <franchise id="0005" score="0.00" gameSecondsRemaining="21600" playersYetToPlay="6" playersCurrentlyPlaying="0" isHome="0">
The XML states <franchise id="0015" score="11.14" gameSecondsRemaining="20004" playersYetToPlay="4" playersCurrentlyPlaying="2"> (This data is for a football game that is currently being played as I'm writing this so the above example may not be exact, but it WON'T be score of 0.00, for example.
Any help would be amazing, thanks!
Have you tried using IMPORTXML? Google Sheets IMPORTXML Page
In IMPORTXML, you can just use the Inspect Element feature to pull the xpath.
Hope this helps. Let me know if I can help further.
Edit: Instructions To Change When Data Is Imported
In the toolbar go to the script editor
Now in the scripts, paste the code listed bellow
/**
* Go through all sheets in a spreadsheet, identify and remove all spreadsheet
* import functions, then replace them a while later. This causes a "refresh"
* of the "import" functions. For periodic refresh of these formulas, set this
* function up as a time-based trigger.
*
* Caution: Formula changes made to the spreadsheet by other scripts or users
* during the refresh period COULD BE OVERWRITTEN.
*
* From: https://stackoverflow.com/a/33875957/1677912
*/
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
// At this point, we are holding the lock.
var id = "YOUR-SHEET-ID";
var ss = SpreadsheetApp.openById(id);
var sheets = ss.getSheets();
for (var sheetNum=0; sheetNum<sheets.length; sheetNum++) {
var sheet = sheets[sheetNum];
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var tempFormulas = [];
for (var row=0; row<formulas.length; row++) {
for (col=0; col<formulas[0].length; col++) {
// Blank all formulas containing any "import" function
// See https://regex101.com/r/bE7fJ6/2
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
if (formulas[row][col].search(re) !== -1 ) {
tempFormulas.push({row:row+1,
col:col+1,
formula:formulas[row][col]});
sheet.getRange(row+1, col+1).setFormula("");
}
}
}
// After a pause, replace the import functions
Utilities.sleep(5000);
for (var i=0; i<tempFormulas.length; i++) {
var cell = tempFormulas[i];
sheet.getRange( cell.row, cell.col ).setFormula(cell.formula)
}
// Done refresh; release the lock.
lock.releaseLock();
}
}
This snippet of code came from Periodically refresh IMPORTXML() spreadsheet function
Last and definitely the least, replace the "YOUR-SHEET-ID"
NOTE: I have not personally tested this code and I cannot vouch for it. I recommend making a copy and testing it there first.
Hopefully, this solves the issue of your data not being imported as often as you want. If you want to manually get "fresh" data, you can just delete/cut the import function and paste it back in.
try in A2:
=ARRAYFORMULA(IFNA(VLOOKUP(C2:C, PlayerList!A:F, {2, 6}, 0)))
and in C2:
=ARRAYFORMULA(QUERY(REGEXEXTRACT(QUERY(IMPORTDATA(
"https://www67.myfantasyleague.com/2019/export?TYPE=liveScoring&L=64741&APIKEY=&W=14&DETAILS=1&JSON=0?273"),
"where Col1 contains 'player id'", 0),
"(player id=""(\d+)).+?(score=""(\d+.\d+))"),
"select Col2,Col4"))
spreadsheet demo

QListView with QStandardItemModel does not show selection highlight through code

I have a QListView that is populated with either a QStandardItemModel or a QStringListModel (based on simplicity of contents... number of columns).
On load, or switching between widgets, I search for the item that should be selected, and try to highlight it.
if (first)
{
m_myListView.setModel(m_standardItemModel);
QList<QStandardItem*> lst = m_standardItemModel->findItems(m_value1, Qt::MatchExactly, 1);
if(!lst.isEmpty())
{
QModelIndex index = lst.at(0)->index();
qDebug() << index.row(); // tells me correct row
//m_myListView.setCurrentIndex(index); // no change if I use
m_myListView.selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
m_myListView.scrollTo(index);
}
}
else
{
m_myListView.setModel(m_stringListModel);
int i = m_stringListModel->stringList().indexOf(m_value2);
if (i >= 0)
{
QModelIndex index = m_stringListModel->index(i);
m_myListView.selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
m_myListView.scrollTo(index);
}
}
The m_stringListModel version correctly highlights (and scrolls to item).
The m_standardItemModel version does not highlight row, and does not scroll to item. But in the uses afterwards, it correctly provides the data for selected index:
QModelIndexList indexList = m_myListView.selectionModel()->selectedIndexes();
if (!indexList.isEmpty())
{
QModelIndex index = indexList.first();
if (index.isValid())
{
row = index.row();
data1 = m_standardItemModel->index(row, 1).data().toString();
...
So... it seems that the selection works, but if it does, why do I not see a highlight ? (and the scrollTo() )
Note - the code is pretty giant but I verified for the possibility of reloading the model and possibly losing the selection - and besides, the QStringListModel version works correctly.
Is that a typical behavior of QStandardItemModel, or is there something I must do, like setting a BackgroundRole type data ?
How can I highlight the selection of the list view with the QStandardItemModel applied ?
I see your code, probably you want to select the first element of your model? Let's try:
void MyClass::selectFirstElement() {
const QModelIndex firsIndex = _myModel->index(0,0);
if (index.isValid())
ui->listView->setCurrentIndex(firstIndex);
ui->listView->scrollTo(firstIndex);
}
}
Could you share the m_standardItemModel implementation? Also configure your list correctly:
ui->listView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->listView->setSelectionBehavior(QAbstractItemView::SelectRows); // Or Columns
Check if your QStandarItem has the selection flag enable. See http://doc.qt.io/qt-4.8/qt.html#ItemFlag-enum for more info.
Finally, you could ensure that the index is stored in the correct model by getting the index in the same row & column directly from the model, something like this:
QModelIndex index = lst.at(0)->index();
index = _model->index(index.row(), index.column());
Sorry, for my poor english :S
Because the item found is different than the display item, the list view is unable to select it...
2 solutions: either create a different QModelIndex from the one found, pointing to the display column, or select an entire row containing the desired index:
m_myListView.selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);

Qt - Tricky search algorithm - duplicates or "mirrored" duplicates in a list

Picture this: I have a JSON file which has a list of Node objects that contain a list of Link types. Something like:
node1:
links: node1,node2
node3,node1
node1,node6
node2:
links: node2,node1
node2,node9
node7,node2
I need to identify unique pairs of links - ie a (node_a,node_b) pair. Note that (node_b,node_a) represents the same thing.
The Link class has getter methods that return a pointer to either the source/destination Node. In the file, the information about the links is stored as a string that has the source node's name and the destination node name, like: (source,destination).
When I build my structure from file, I first create the Nodes and only then I create the Links. The Link constructor is as follows:
Link::Link(Node *fromNode, Node *toNode)
And my code to create the links:
QList<Link*> consumedLinks; // list where I was trying to place all the non-duplicate links
// for each node in the file
foreach(QVariant nodesMap, allNodesList){
QVariantMap node1 = nodesMap.toMap();
QList<QVariant> nodeDetails = node1["node"].toList();
QVariantMap allLinksMap = nodeDetails.at(9).toMap();
// extract all the links of the node to a list of QVariant
QList<QVariant> linksList = allLinksMap["links"].toList();
// for each Link in that list
foreach(QVariant linkMap, linksList){
QVariantMap details = linkMap.toMap();
Node *savedFromNode;
Node *savedToNode;
// get all the Items in the scene
QList<QGraphicsItem*> itemList = scene->items();
// cast each item to a Node
foreach(QGraphicsItem *item, itemList){
Node* tempNode = qgraphicsitem_cast<Node*>(item);
if(tempNode){
// check what the node name matches in the link list
if(tempNode->text()==details["fromNode"]){
savedFromNode = tempNode;
}
if(tempNode->text()==details["toNode"]){
savedToNode = tempNode;
}
}
}
// create the link
Link *linkToCheck = new Link(savedFromNode,savedToNode);
// add it to the links list (even if duplicate)
consumedLinks.append(linkToCheck);
}
}
// add all the links as graphics items in the scene
foreach(Link *linkToCheck, consumedLinks){
scene->addItem(linkToCheck);
}
So right now this doesn't check for duplicates in the consumedLinks list (obviously). Any ideas on how to achieve this?
NOTE: I know that the pseudo-JSON above isn't valid, it's just to give you an idea of the structure.
NOTE2: I rephrased and added detail and code to the question to make it clearer to understand what I need.
1. Normalize links i.e. replace (a, b) to (b, a) when b < a. Each link should be represented as (a, b), a < b.
2. Create a QSet< QPair<Node*, Node *> > variable and put all links into it. Each link object can be maked using qMakePair(node1, node2). Since QSet is an qnique container, all duplicates will be removed automatically.
Well, I didn't exactly do the same as #Riateche suggested, but something similar.
I basically created a QList<QPair<QString,QString> > so I could check for duplicates easily and then I created a list of Link based on the items in the QPair list. Something like:
QList<Node*> existingNodes;
QList<QPair<QString,QString> > linkPairList;
QList<QGraphicsItem*> itemList = scene->items();
foreach(QGraphicsItem *item, itemList){
Node* tempNode = qgraphicsitem_cast<Node*>(item);
if(tempNode){
existingNodes.append(tempNode);
}
}
foreach(QVariant nodesMap, allNodesList){
QVariantMap node1 = nodesMap.toMap();
QList<QVariant> nodeDetails = node1["node"].toList();
QVariantMap allLinksMap = nodeDetails.at(9).toMap();
QList<QVariant> linksList = allLinksMap["links"].toList();
foreach(QVariant linkMap, linksList){
QVariantMap details = linkMap.toMap();
QPair<QString,QString>linkPair(details["fromNode"].toString(),details["toNode"].toString());
QPair<QString,QString>reversedLinkPair(details["toNode"].toString(),details["fromNode"].toString());
if(!linkPairList.contains(linkPair)){
// !dupe
if(!linkPairList.contains(reversedLinkPair)){
// !reversed dupe
linkPairList.append(linkPair);
}
}
qDebug()<<"number of pairs: "<<linkPairList.size();
}
}
QPair<QString,QString> linkPairInList;
foreach(linkPairInList, linkPairList){
Node *savedFromNode;
Node *savedToNode;
foreach(Node* node, existingNodes){
if(node->text()==linkPairInList.first){
savedFromNode=node;
}
if(node->text()==linkPairInList.second){
savedToNode=node;
}
}
Link *newLink = new Link(savedFromNode,savedToNode, "input");
scene->addItem(newLink);
}
This is the correct answer because it solves what I needed to solve. Since I only got there because of #Riateche's comments, I +1'd his answer.

Set inner HTML using QDomDocument

How can I set the inner HTML in a QDomElement?
When I’m using QWebElement I have the method QWebElement::setInnerXml, there is some similar method in QDomElement?
There's no API to "inject" XML snippets as text into a QDomDocument (or QXmlStreamWriter). One has to use the API and create the nodes programmatically.
Assuming you have a string to start with, my current solution is to generate a DOM tree from it, import that tree in a fragment, then copy those in the right place (you must have an import which is why you need an intermediate fragment. Quite unfortunate if you ask me.)
// assuming that 'n' is the node you are replacing or at least inserting after
// 'parent' is the parent of 'n'
// 'result' is the string to replace 'n' or insert after 'n'
QDomDocument doc_text("snap");
doc_text.setContent("<text>" + result + "</text>", true, NULL, NULL, NULL);
QDomDocumentFragment frag(xml.createDocumentFragment());
frag.appendChild(xml.importNode(doc_text.documentElement(), true));
QDomNodeList children(frag.firstChild().childNodes());
const int max(children.size());
QDomNode previous(n);
for(int i(0); i < max; ++i)
{
QDomNode l(children.at(0));
parent.insertAfter(children.at(0), previous);
previous = l;
}
// if you are replacing, then delete node n as well
parent.removeChild(n);
Note that the <text> tag is used so that way result does not need to be a tag, it could just be text and it will still work.
Obviously, if you have a fragment or XML from another document to start with, ignore the code that creates that code in the doc_text object.

Resources