I used to do it by attaching an object
self.page().mainFrame().addToJavaScriptWindowObject("js_interface", self.jsi)
In 5.7 I do:
self.page().setWebChannel(self.jsi)
But I understandibly get a JavaScript error when I try to access exposed functions:
js: Uncaught ReferenceError: js_interface is not defined
Googling around I found that I should use qwebchannel.js, but I couldn't find the file or instructions on how to use it anywhere (there was some info, but only in some examples provided when installing QT, not PyQT).
You can include qwebchannel.js into html page using the script tag:
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
Then, create a web channel on the python side:
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView
class CallHandler(QObject):
#pyqtSlot()
def test(self):
print('call received')
view = QWebEngineView()
channel = QWebChannel()
handler = CallHandler()
channel.registerObject('handler', handler)
view.page().setWebChannel(channel)
JS code that interacts with the web channel:
new QWebChannel(qt.webChannelTransport, function (channel) {
window.handler = channel.objects.handler;
window.handler.test();
});
Take a look at this page. It contains a useful example (in c++ but easily translatable into python).
First of all, you have to use a websocket to communicate from html to your app and viceversa.
Then you can set up your QWebChannel.
I think it's big drawback, JS cannot directly communicate with Python in PyQT5.9+ like it used to with "addToJavaScriptWindowObject" command.
And using websockets... what if Firewall is heavy and all ports blocked.
I guess I will rely on simple callback (long pooling type from Python to JS checking for changes/commands) method and no QTWebChannel usage.
Related
maybe it is because I am not handy with js, but how can I load a .json file from geth console? I want this in order to avoid the clumpsy way of copy-paste each raw abi content for each one of the contracts var abi_1 = [...]; var abi_2 = [...]; .... I know the console is javascript, so I tried with require (easy with nodejs), but it doesn't work. It is impossible in geth (js console) to load an abi_1.json and store it in a variable abi_1 in the same way I easily pickle a file in python? Thank you and hope this question makes sense to the community.
As specified in the documentation[1], the geth console uses an implementation of ECMAScript in go called goja[2]. From what I know, classical JS (so no nodejs) does not have any IO functionnalities...
However, maybe you could use the 'loadScript' function available in the console + some bash.
For instance, let's say that your JSON file is located in /tmp/abi.json. All your JS operations can be stored in another file (let's say /tmp/operations.js).
You could use geth attach http://localhost:8545 --exec "var abi = $(cat /tmp/abi.json); loadScript('/tmp/operations.js')"
For example :
/tmp/file.json contains { 'test': 'Hello, world'}
geth attach http://localhost:8545 --exec "var a = $(cat /tmp/file.json); console.log(a.test)"
Would print Hello, world!
That's not a perfect solution but it could be convenient to you.
[1] https://geth.ethereum.org/docs/interface/javascript-console
[2] https://github.com/dop251/goja
I have a non-traditional, image upload button on my company's website. I want to have an automated way to upload an image using this button, but without having to use a tool like AutoIt in order to interact with the file explorer.
Here's a sample of this button's HTML:
<button ng-click="onClick()" ng-disabled="readOnly" accepted-types="image/*" on-files-selected="onFilesSelected" allow-multiple="true" readonly="readonly">Add images</button>
It's a bit different than the usual input element, e.g. <input type="file">, and it's using AngularJS. Since it's not an input element, I don't think I can use Selenium's sendKeys() function to input the image's file location on my machine.
Is there any hack or workaround to uploading the image? I was considering things like overwriting the onClick() function to do read from a specified location (this approach doesn't really seem like it's doable), or possibly intercepting the event that opens the file explorer and trying to hack my way from there, but these are all just unsupported and untested approaches to solving the problem.
Would it be possible to do this in another browser-automation tool, like Microsoft's Playwright?
Use JACOB it provides java native interface where you can use AutoIt functionalities with selenium here is a sample I am using it in most of the places like MSTeams,Slack for Automation[Upload Feature] it does the job.
List of Steps you need to do before jumping to the code:
Step 1:
Download JACOB jar
Step 2:
Register the AutoIt COM libraries e.g regsvr32 AutoItX3_x64.dll
Use these in your code
jacob.jar
AutoItX4Java.jar
jacob-1.18-x64.dll
jacob-1.18-x86.dll
Sample Code:
[This Code Interacts with file explorer]
import com.jacob.com.LibraryLoader;
import autoitx4java.AutoItX;
public class Attachments {
public void uploadAttachments(){
File f = new File("Location");
File[] fil =f.listFiles();
//Upload Button Xpath
WebElement uploadFromComp = driver.findElement(By.xpath("//span[contains(text(),'Upload from my computer')]"));
uploadFromComp.click();
Thread.sleep(5000);
String jacobDllVersionToUse;
if (jvmBitVersion().contains("32")) {
jacobDllVersionToUse = "jacob-1.19-x86.dll";
} else {
jacobDllVersionToUse = "jacob-1.19-x64.dll";
}
File file1 = new File("registerAutoItDll", jacobDllVersionToUse);
System.setProperty(LibraryLoader.JACOB_DLL_PATH, file1.getAbsolutePath());
AutoItX x = new AutoItX();
x.winWaitActive("Open");
x.sleep(5000);
x.send(fil[j].getAbsolutePath());
x.send("{ENTER}", false);
}}
I hope it works for you.
It is 100% posible with playwright and it is lot simplier then in the Selenium.
// Select one file
await page.setInputFiles('input#upload', 'myfile.pdf');
// Select multiple files
await page.setInputFiles('input#upload', ['file1.txt', 'file2.txt']);
See more on:
https://playwright.dev/docs/input#upload-files
I wanna use the next java script code:
function dumpVal(file) {
if (file !=null) {
var wb = new Workbook.create(file);
var sheet = wb.getSheet("Tabelle1");
for (myrow = 1; !isCellEmpty(sheet, myrow, 0); myrow++) {
dataset.setColumnValue("A",getNumericValue(sheet,myrow,0));
dataset.storeResultRow();
}
}
return;
}
but when I am compiling it I receive the next error message: ReferenceError: "Workbook" is not defined.
Can someone to tell me what am I doing wrong?
You recieve the error with the followig line of code:
var wb = new Workbook;
At the point where you create the workbook (new Workbook) you refer to a class called "Workbook". At this point your script dont have a class called Workbook it.
Solution:
You should check your scripts if the class is included and its naming.
Maybe the class is initialized later!
For debug purposes you can try to create the class a line before:
class Workbook{ }
If you recieve an error now because Workbook needs a method called "create", you know that the class is just missing.
I assume that you want to parse excel file from your web application, the library that you are using is for developing add-ins for excel not web application :
Excel JavaScript API programming overview
This article describes how to use the Excel JavaScript API to build add-ins for Excel 2016. It introduces key concepts that are fundamental to using the APIs, such as RequestContext, JavaScript proxy objects, sync(), Excel.run(), and load(). The code examples at the end of the article show you how to apply the concepts.
source :
https://dev.office.com/docs/add-ins/excel/excel-add-ins-javascript-programming-overview
If you want to parse Excel in your web application I suggest to use this library :
https://github.com/SheetJS/js-xlsx
I did not use it so I cant garanty it, but you can look for similar librarys.
I have a Win32-DLL (C++) which is loaded as a plugin in another application. The DLL starts a nw.js instance (ShellExecuteEx and SEE_MASK_NOCLOSEPROCESS) and ends it at DLL unloading (by the hInstance of ShellExecuteEx). I need a way to send a string (plain ansi) to the nw-process and retrieve an answer (also string). The old way was a simple http-request with the response in the body. But the environment changes during the development, the "package" app-dll-nw runs multiple times by the same user and multiple users run on the same machine (terminal server). So port listing is "impossible" (yeah random ports or singleton nw, but no).
I found different ways:
socket - port listing problem
wm_copydata/wm_... - need a custom nw-plugin with hidden window (no native nw way); no request-response-system
RPC - port listing problem
DDE - no native javascript way (found a module, which uses .net); In my old delphi days DDE was a not so simple task and it failed multiple times with no logic.
shared memory - no experience; expectations: asynchronous, trigger?, no native javascript way
shared file - no experience; expectations: asynchronous, trigger (watcher on file change) but problems with synchronization, native js way possible
named pipe - no experience; expectations: win32-api and like a chat system (in-pipe [send broadcast] and out-pipe [receive broadcast], or both in one)? If yes, I can use one name about all instances and use unique identifiers and wait for the right answer.
What is a nice and simple way to communicate like the http-way but w/o networking?
Update 1: The node module "net" is able to create a server for a named pipe. The first test, sending a string from the dll to nw, was successful.
var server = net.createServer(function(stream) {
stream.on('data', function(c) {
console.log('data:', c.toString());
});
stream.on('end', function() {
//server.close();
});
});
server.listen('\\\\.\\pipe\\MyAppDynamicGUID');
Update 2 - My Solution
With named pipe and a simplified version of https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx I found a working methode.
Server in nw.js:
var server = net.createServer(function(req) {
req.on('data', function(c) {
console.log(c.toString());
req.write('123|Hello World', 'ascii');
});
});
server.listen('\\\\.\\pipe\\MyAppDynamicGUID');
The client in C++ (no permanent connection, strange string handling, simplified error handling):
static std::string PipenameA = "\\\\.\\pipe\\MyAppDynamicGUID";
#define BUFSIZE 512
std::string SendPipeRequestA(std::string sRequest) {
DWORD dwToWrite, dwWritten, dwRead;
BOOL bSuccess;
char chBuf[BUFSIZE];
std::vector<char> buffer;
HANDLE hPipe = CreateFileA(PipenameA.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE)
return "-1|Pipe-Error 1 (connect)";
dwToWrite = (lstrlenA(sRequest.c_str())+1)*sizeof(char);
bSuccess = WriteFile(hPipe, sRequest.c_str(), dwToWrite, &dwWritten, NULL);
if (!bSuccess)
return "-1|Pipe-Error 2 (write)";
do {
bSuccess = ReadFile(hPipe, chBuf, BUFSIZE*sizeof(char), &dwRead, NULL);
if (!bSuccess && GetLastError() != ERROR_MORE_DATA)
break;
buffer.insert(buffer.end(), chBuf, chBuf + dwRead);
} while (!bSuccess);
std::string sResponse(&buffer[0]);
CloseHandle(hPipe);
return sResponse.c_str();
}
// Jonny
The answers you will get will be opinion based, be aware of that.
you can inject the data into the JS module as command line argument
for example
start nw.js MyData
and get it insinde the JS with process.argv.
now, sending the data back to the C++ executables/DLLs is a bit tricky.
if you shell-execute the process, you can have the handle to it.
you can print the data into the stdout from the JS part , and read it in the native app by getting the STDOUT handle from the process handle.
Register your nw.js app with a custom url should be an elegant way.
Such as "github://", "thunder://", "twitter://"
On windows you may have a look at:
https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
With custom url you can take simple arguments to nw.js at single-instance mode. See:
https://github.com/nwjs/nw.js/wiki/Handling-files-and-arguments#open-file-with-existing-app
If more data required maybe base64 can help, or even more by LZ-String compress method.
There is a software product called AnyChart which is great for embedding Flashed based charts in web pages. AnyCharts can also export to PNG file format. Here is an example:
<script type="text/javascript" language="javascript">
//<![CDATA[
var chart = new AnyChart('http://www.mysite.com/swf/AnyChart.swf');
chart.width = 600;
chart.height = 300;
chart.setXMLFile('http://www.mysite.com/anychart.xml');
chart.addEventListener("draw", function() { saveChartAsImage(chart); });
chart.write("content-box");
//]]>
</script>
My ultimate goal is to make a automated service to export the AnyChart charts to PNG format. So I made a service with Indy which calls pages containing the AnyChart javascript. But the problem seems to be that Indy cannot execute the javascript.
Is there a way to enable Indy to execute javascript?
No, Indy does not execute Javascript. You may have also noticed that it doesn't parse or display HTML, and it doesn't run Flash, either. Indy does network protocols.
You could import the Microsoft Script Control ActiveX object and have that run your Javascript. If you need details on that, post a new question.
You don't have to use Indy for this. If you want you can use TWebBrowser.
IHTMLWindow2 interface has execScript function. So may be you can :
var
Doc : IHTMLDocument2;
Win : IHTMLWindow2;
aBrowser : TWebBrowser;
//...
begin
//...
Doc := aBrowser.Document as IHTMLDocument2;
Win := Doc.parentWindow;
Win.execScript('alert(SomeMessage);', 'JavaScript');
end;
Did you try vcl FOR THE web (aka Intraweb atozed) ?
There is a teechart version wich is quite useful, you can also execute "external" javascript code within any of the TiwForms of your web app (the exact same code you are using now).
Post a new question if you need to and I'll be glad to help.