queue addlistener events or place event on EDT

2 visualizaciones (últimos 30 días)
Jim Hokanson
Jim Hokanson el 24 de Nov. de 2017
Respondida: Jim Hokanson el 5 de Feb. de 2018
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 comentarios
Jim Hokanson
Jim Hokanson el 27 de Nov. de 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 el 9 de En. de 2018
Editada: Jim Hokanson el 15 de En. de 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.

Iniciar sesión para comentar.

Respuesta aceptada

Jim Hokanson
Jim Hokanson el 5 de Feb. de 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();

Más respuestas (1)

Jim Hokanson
Jim Hokanson el 26 de Nov. de 2017
Editada: Jim Hokanson el 27 de Nov. de 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

Categorías

Más información sobre App Building en Help Center y File Exchange.

Community Treasure Hunt

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

Start Hunting!

Translated by