queue addlistener events or place event on EDT

3 vues (au cours des 30 derniers jours)
Jim Hokanson
Jim Hokanson le 24 Nov 2017
Every time the xlim changes I want to run some code.
plot(1:100)
L = addlistener(gca, 'XLim', 'PostSet', @my_callback);
If I use a listener, I seem to miss events if the callback is still running.
Thus I would like to queue property change event callbacks. Is this possible?
Alternatively, is it possible to have the callback queue a function on the EDT (event-dispatch thread) so that any changes are not missed?
The only really ugly solution I can come up with involves using the 'PropertyChangeCallback' of a java class since it seems to respond to being set in Matlab, as opposed to uicontrols which don't throw callbacks (I think) when changing their values via Matlab.
Update:
It appears that this is dependent on the callback code.
I had something like this:
disp(now)
t = tic;
pause(3)
toc(t);
If you do something like a horizontal zoom 2 - 3 times (quickly enough), only the first listener callback will run, even though you've changed xlim multiple times.
However if instead the code is:
disp(now)
t = tic;
r = rand(1,1e8);
toc(t);
Then rendering will not occur until the code has finished executing. Here I am using the rand() command to cause a reasonable delay (on my laptop 1.5s)
So in other words the blocking behavior of listeners depends on the callback code.
The missed events also only occur with user input. When running code the events run sequentially. For example, try this with the 'pause' version of the callback. In my case I get three events, rather than just 1.
set(gca,'xlim',[0 10]);
set(gca,'xlim',[0 20]);
set(gca,'xlim',[0 30]);
  4 commentaires
Jim Hokanson
Jim Hokanson le 27 Nov 2017
I tried the following and it only registers the first event, not subsequent ones:
clf
ax = gca;
plot(1:100)
L = addlistener(gca, 'XLim', 'PostSet', @my_callback);
set(ax,'Interruptible','off','BusyAction','queue')
%Inside my_callback
disp(now)
t = tic;
pause(3)
toc(t);
Jim Hokanson
Jim Hokanson le 9 Jan 2018
Modifié(e) : Jim Hokanson le 15 Jan 2018
Just to follow up on this before I submit for technical support since I'm not getting the performance that I want. I want the following behavior:
1) Ensure that a function has always run after the xlim property changes. I don't need it to execute following every change, but if some time has passed with no change (a few hundred milliseconds), I want my function to have run after the change. More specifically, I can't miss a terminal event because of the function running.
2) It should not block other code execution for long periods of time. One approach to getting the last change is to check the xlim property as the function is about to terminate. If the property has changed, the function could run again. However, this could go on for a while and it should persistently block execution of other code, which is not desirable.
3) Indications of some rendering should be nearly immediate, even if the final processing is delayed.

Connectez-vous pour commenter.

Réponse acceptée

Jim Hokanson
Jim Hokanson le 5 Fév 2018
I found a different solution which appears to work nicely: https://undocumentedmatlab.com/blog/matlab-and-the-event-dispatch-thread-edt
I don't understand the blog post details but below is what I am using based on the callbackOnEDTQueue() function referenced at the end of the post.
so now in initialize:
obj.L3 = addlistener(axes_handle.XRuler,'MarkedClean',@(~,~)obj.xrulerMarkedClean);
obj.callback_obj = handle(com.mathworks.jmi.Callback,'callbackProperties');
set(obj.callback_obj,'delayedCallback',@(~,~)obj.renderDataCallback());
xRulerMarkerClean
if isequal(obj.last_processed_xlim,get(obj.axes_handle,'XLim'))
return
end
obj.throwCallbackOnEDT();
throwCallbackOnEDT()
%Some logging not shown, but the main call is:
obj.callback_obj.postCallback();

Plus de réponses (1)

Jim Hokanson
Jim Hokanson le 26 Nov 2017
Modifié(e) : Jim Hokanson le 27 Nov 2017
Here's one (ugly) solution.
This setup function does the following:
  1. Sets up listeners that I care about.
  2. Attaches a Java button to a figure which I've hidden ('Visible','off','HandleVisibility','off')
  3. Sets up the actual callback that I want on the Java button
function initialize(obj,axes_handle)
[obj.j_comp, temp] = javacomponent('javax.swing.JButton',[],obj.fig_handle);
obj.h_container = handle(temp);
set(obj.h_container,'BusyAction','queue','Interruptible','off');
obj.axes_handle = axes_handle;
if verLessThan('matlab', '8.4')
size_cb = {'Position', 'PostSet'};
else
size_cb = {'SizeChanged'};
end
obj.L1 = addlistener(axes_handle,'XLim','PostSet', @(~,~)obj.listenerCallback);
obj.L2 = addlistener(axes_handle, size_cb{:}, @(~,~) obj.listenerCallback);
set(obj.j_comp,'PropertyChangeCallback',@(~,~)obj.renderDataCallback());
end
The listener code is as follows. By toggling the text we generate the callbacks of interest on the EDT.
function listenerCallback(obj)
%Note the try statement because j_comp gets deleted asynchronously
try
%fprintf('1 %s\n',mat2str(get(obj.axes_handle,'xlim')));
if obj.last_string_index == 1
obj.last_string_index = 2;
obj.j_comp.setText('a');
else
obj.last_string_index = 1;
obj.j_comp.setText('b');
end
%fprintf('2 %s\n',mat2str(get(obj.axes_handle,'xlim')));
end
end
end
  1 commentaire
Jim Hokanson
Jim Hokanson le 27 Nov 2017
I should also mention more control over callback contents could be obtained by following:

Connectez-vous pour commenter.

Catégories

En savoir plus sur App Building dans Help Center et File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by