- Undocumented Matlab - https://undocumentedmatlab.com -
Matlab callbacks for uifigure JavaScript events
Posted By Yair Altman On August 15, 2018 | 5 Comments
I would like to welcome back guest blogger Iliya Romm of Israel’s Technion Turbomachinery and Heat Transfer Laboratory [1]. Today Iliya will discuss how to assign Matlab callbacks to JavaScript events in the new web-based uifigures. Other posts on customizations of web-based Matlab GUI can be found here [2].
On several occasions (including the previous post [3] by Khris Griffis) I came across people who were really missing the ability to have Matlab respond to various JavaScript (JS) events. While MathWorks are working on their plans to incorporate something similar to this in future releases, we’ll explore the internal tools already available, in the hopes of finding a suitable intermediate solution.
Today I’d like to share a technique I’ve been experimenting with, allowing Matlab to respond to pretty much any JS event to which we can attach a listener. This is an overview of how it works:
webWindow
, from within the Matlab callback, to retrieve any additional information (“payload”) that the JS passed.This approach allows, for example, to easily respond to mouse events:
jsEventDemo(demoNum)
, where demoNum
is 1..4. Note: this code was developed on R2018a, unfortunately I cannot guarantee it works on other releases.
function varargout = jsEventDemo(demoNum)
% Handle inputs and outputs
if ~nargin
demoNum = 4;
end
if ~nargout
varargout = {};
end
% Create a simple figure:
hFig = uifigure('Position',[680,680,330,240],'Resize','off');
hTA = uitextarea(hFig, 'Value', 'Psst... Come here...!','Editable','off');
[hWin,idTA] = mlapptools.getWebElements(hTA);
% Open in browser (DEBUG):
% mlapptools.waitForFigureReady(hFig); mlapptools.unlockUIFig(hFig); pause(1);
% web(hWin.URL,'-browser')
% Prepare the JS command corresponding to the requested demo (1-4)
switch demoNum
% Demo #1: Respond to mouse events, inside JS, using "onSomething" bindings:
case 1
% Example from: https://dojotoolkit.org/documentation/tutorials/1.10/events/#dom-events
jsCommand = sprintf(fileread('jsDemo1.js'), idTA.ID_val);
% Demo #2: Respond to mouse click events, inside JS, using pub/sub:
case 2
% Example from: https://dojotoolkit.org/documentation/tutorials/1.10/events/#publish-subscribe
hTA.Value = 'Click here and see what happens';
jsCommand = sprintf(fileread('jsDemo2.js'), idTA.ID_val);
% Demo #3: Trigger Matlab callbacks programmatically from JS by "pressing" a fake button:
case 3
hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0], ...
'ButtonPushedFcn', @fakeButtonCallback);
[~,idB] = mlapptools.getWebElements(hB);
jsCommand = sprintf(fileread('jsDemo3.js'), idTA.ID_val, idB.ID_val);
% Demo 4: Trigger Matlab callbacks and include a "payload" (i.e. eventData) JSON:
case 4
hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0],...
'ButtonPushedFcn', @(varargin)smartFakeCallback(varargin{:}, hWin));
[~,idB] = mlapptools.getWebElements(hB);
jsCommand = sprintf(fileread('jsDemo4.js'), idTA.ID_val, idB.ID_val);
end % switch
% Execute the JS command
hWin.executeJS(jsCommand);
end
% Matlab callback function used by Demo #3
function fakeButtonCallback(obj, eventData) %#ok
disp('Callback activated!');
pause(2);
end
% Matlab callback function used by Demo #4
function smartFakeCallback(obj, eventData, hWin)
% Retrieve and decode payload JSON:
payload = jsondecode(hWin.executeJS('payload'));
% Print payload summary to the command window:
disp(['Responding to the fake ' eventData.EventName ...
' event with the payload: ' jsonencode(payload) '.']);
% Update the TextArea
switch char(payload.action)
case 'enter', act_txt = 'entered';
case 'leave', act_txt = 'left';
end
str = ["Mouse " + act_txt + ' from: '; ...
"(" + payload.coord(1) + ',' + payload.coord(2) + ')'];
obj.Parent.Children(2).Value = str;
end
Several thoughts:
%s
with valid widget IDs. Of course, these could be made into proper JS functions.jsCommand
variable, as a Matlab string (char array)textarea
control [6], so that we would get the payload right in the callback’s eventData
object in Matlab, Unfortunately, I couldn’t get it to fire programmatically (solutions like this [7] didn’t work). So instead, I store the payload as JSON, and retrieve it with jsondecode(hWin.executeJS('payload'))
in the smartFakeCallback
function.
require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
function(on, dom, domStyle, mouse) {
var myDiv = dom.byId("%s");
on(myDiv, mouse.enter, function(evt){
domStyle.set(myDiv, "backgroundColor", "red");
});
on(myDiv, mouse.leave, function(evt){
domStyle.set(myDiv, "backgroundColor", "");
});
});
require(["dojo/on", "dojo/topic", "dojo/dom"],
function(on, topic, dom) {
var myDiv = dom.byId("%s");
on(myDiv, "click", function() {
topic.publish("alertUser", "Your click was converted into an alert!");
});
topic.subscribe("alertUser", function(text){
alert(text);
});
});
require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
function(on, dom, domStyle, mouse) {
var myDiv = dom.byId("%s");
var fakeButton = dom.byId("%s");
on(myDiv, mouse.enter, function(evt){
fakeButton.click();
});
});
var payload = [];
require(["dojo/on", "dojo/dom", "dojo/topic", "dojo/mouse"],
function(on, dom, topic, mouse) {
var myDiv = dom.byId("%s");
var fakeButton = dom.byId("%s");
topic.subscribe("sendToMatlab", function(data){
payload = data;
fakeButton.click();
});
on(myDiv, mouse.enter, function(evt){
data = {action: "enter",
coord: [evt.clientX, evt.clientY]};
topic.publish("sendToMatlab", data);
});
on(myDiv, mouse.leave, function(evt){
data = {action: "leave",
coord: [evt.clientX, evt.clientY]};
topic.publish("sendToMatlab", data);
});
});
As you can see, this opens some interesting possibilities, and I encourage you to experiment with them yourself! This feature will likely be added to the mlapptools
toolbox [12] as soon as an intuitive API is conceived.
If you have any comments or questions about the code above, or just want to tell me how you harnessed this mechanism to upgrade your uifigure (I would love to hear about it!), feel free to leave a message below the gist [13] on which this post is based (this way I get notifications!).
Categories: Figure window, Guest bloggers, GUI, High risk of breaking in future versions, Undocumented feature
Article printed from Undocumented Matlab: https://undocumentedmatlab.com
URL to article: https://undocumentedmatlab.com/articles/matlab-callbacks-for-uifigure-javascript-events
URLs in this post:
[1] Turbomachinery and Heat Transfer Laboratory: http://bcukurel.net.technion.ac.il
[2] found here: https://undocumentedmatlab.com/blog/tag/uifigure
[3] the previous post: https://undocumentedmatlab.com/blog/customizing-web-gui-uipanel
[4] direct download link: http://undocumentedmatlab.com/files/jsEventDemo.m
[5] files: #JS
[6] textarea
control: https://www.w3schools.com/tags/tag_textarea.asp
[7] like this: https://stackoverflow.com/a/36648958/3372061
[8] direct download link: https://gist.github.com/Dev-iL/7bd222efb01b3bf9fc0ed5d5645d53e7/raw/872d3d7d00a7b2e18e87257e0ff7c2b6efaa4e45/jsDemo1.js
[9] direct download link: https://gist.github.com/Dev-iL/7bd222efb01b3bf9fc0ed5d5645d53e7/raw/872d3d7d00a7b2e18e87257e0ff7c2b6efaa4e45/jsDemo2.js
[10] direct download link: https://gist.github.com/Dev-iL/7bd222efb01b3bf9fc0ed5d5645d53e7/raw/872d3d7d00a7b2e18e87257e0ff7c2b6efaa4e45/jsDemo3.js
[11] direct download link: https://gist.github.com/Dev-iL/7bd222efb01b3bf9fc0ed5d5645d53e7/raw/872d3d7d00a7b2e18e87257e0ff7c2b6efaa4e45/jsDemo4.js
[12] mlapptools
toolbox: https://github.com/StackOverflowMATLABchat/mlapptools
[13] the gist: https://gist.github.com/Dev-iL/7bd222efb01b3bf9fc0ed5d5645d53e7
[14] Matlab callbacks for Java events : https://undocumentedmatlab.com/articles/matlab-callbacks-for-java-events
[15] Matlab callbacks for Java events in R2014a : https://undocumentedmatlab.com/articles/matlab-callbacks-for-java-events-in-r2014a
[16] Detecting window focus events : https://undocumentedmatlab.com/articles/detecting-window-focus-events
[17] Enabling user callbacks during zoom/pan : https://undocumentedmatlab.com/articles/enabling-user-callbacks-during-zoom-pan
[18] Capturing print events : https://undocumentedmatlab.com/articles/capturing-print-events
[19] Waiting for asynchronous events : https://undocumentedmatlab.com/articles/waiting-for-asynchronous-events
[20] : https://github.com/StackOverflowMATLABchat/mlapptools/issues
[21] : https://chat.stackoverflow.com/rooms/81987/chatlab-and-talktave
[22] : http://localhost:4040/
Click here to print.
Copyright © Yair Altman - Undocumented Matlab. All rights reserved.
5 Comments To "Matlab callbacks for uifigure JavaScript events"
#1 Comment By Khris Griffis On August 23, 2018 @ 21:34
Great workaround Iliya!
I was playing with something similar to this, though my approach was not as nice. I found that we can add listeners to the DOM for key presses and then use javascript (
document.querySelector([id="uniqName_##_#"]').click()
) to initiate a button press on an invisible button, which then fires the matlab callback for the .ButtonPushedFcn. Essentially doing what you’ve done here for mouse clicks but for keyboard presses. I haven’t extensively tested it, but the following function works to spit out keypresses into the command window (only tested on 2018a).I’m sure there are better ways to capture the keypress information (dojo/keys perhaps) and I would assume it’s possible to capture keypresses in defined contexts, like when a text area is active or when the mouse is hovering over an element, but I haven’t really tested this extensively.
Cheers,
Khris
#2 Comment By Iliya On August 24, 2018 @ 15:38
Hi Khris,
Thanks for sharing your attempt (and thoughts) regarding the key press listeners! I’m sure it would be a helpful starting point for those who would like to explore this direction further. If you have a suggestion regarding how to add this functionality to
mlapptools
in a way that would be convenient for the user – please don’t hesitate to [20] in the repo and/or drop us a line in [21]. We’d be happy to hear feedback regarding any existing (or missing) functionality, and ideas about alternative implementations.
All the best,
Iliya.
#3 Comment By Perttu On December 2, 2018 @ 19:29
Hi,
I don’t know if this is “general knowledge” but you can turn on debugging by running:
before any other App, or kill all MatlabWindow applications (from task manager) before the command. Then go with Chrome to [22]
#4 Comment By Manu On October 4, 2019 @ 19:26
Hi,
thanks very much for this code. Working perfectly for me on a uiimage object which I use to display a tiled map (like OpenStreetMap).
Since I needed more possible interactions than just the default MATLAB “ImageClicked” callback, this came as the perfect solution!
Thanks again, great work.
#5 Comment By Ba On April 3, 2023 @ 09:05
Dear Iliya,
remove uibutton, instead of using the “WindowButtonDownFcn” of uifigure is working for me.