2016-07-14 42 views
-1

Mein Programm führt eine Funktion aus, wenn der Benutzer auf ein Achsenobjekt klickt. Diese Funktion verwendet die Position des Cursors und zeigt dessen Fortschritt als Animation an. Was ich brauche, ist, den laufenden Aufruf der Funktion zu stoppen, wenn der Benutzer auf eine neue Position klickt, und dann die Funktion für diese neue Position aufruft.Wie warte ich auf die Beendigung einer laufenden Funktion in einer GUI-Callback-Funktion?

Mein Code ist so etwas (in meinem ursprünglichen Code, den ich guidata und handles anstelle von globalen Variablen verwenden):

function TestUI 
clc; clear variables; close all; 
figure; axis equal; hold on; 
xlim([0 100]); ylim([0 100]); 
set(gca, 'ButtonDownFcn', @AxisButtonDownFcn); 
global AnimateIsRunning 
AnimateIsRunning = false; 
end 

function AxisButtonDownFcn(ah, ~) 
C = get(gca,'CurrentPoint'); 
global xnow ynow AnimateIsRunning 
xnow = C(1, 1); ynow = C(1, 2); 
if AnimateIsRunning 
    % ---> I need to wait for termination of currently running Animate 
end; 
Animate(ah, xnow, ynow); 
end 

function Animate(ah, x, y) 
T = -pi:0.02:pi; r = 5; 
global xnow ynow AnimateIsRunning 
AnimateIsRunning = true; 
for t = T 
    if ~((xnow==x)&&(ynow==y)) 
     return; 
    end; 
    ph = plot(ah, x+r*cos(t), y+r*sin(t), '.'); 
    drawnow; 
    delete(ph) 
end 
AnimateIsRunning = false; 
end 

Mein Problem ist, dass alle neueren Klicks zur Zeit Funktion läuft Interrupt und hält vorherigen Lauf Animate in ein Stapel. Dadurch bleibt die letzte Zeichnung der vorherigen Animation sichtbar. Das Schlimmste ist, dass die Größe des Stacks 8 zu sein scheint und neuere Unterbrechungen in einer Warteschlange gespeichert werden! Bedeutung Benutzer kann Position nur 8 mal aktualisieren. Um das Problem zu sehen, können Sie das obige Codebeispiel ausführen und wiederholt auf das Achsenobjekt klicken.

Jetzt möchte ich prüfen, ob Animate läuft in AxisButtonDownFcn, und warten auf seine Beendigung (oder beende es mit Gewalt), und rufen Sie dann Animate mit neuen Parametern.

+0

Möchten Sie alle neuen Klicks verbieten, bis sie fertig sind? – Suever

+0

@Suever Nein, ich möchte auf 'Animate' warten, um seine Arbeit zu beenden, es wird' xnow' und 'ynow' in der nächsten Iteration prüfen und zurückkehren. – saastn

Antwort

0

Als memyself die other question beantwortet, ist es unmöglich, derzeit Animate[oder warten auf ihre Beendigung], weil beide AxisButtonDownFcn und Animate laufen zu beenden in demselben Thread aufgerufen werden. So verfügbaren Optionen sind:

  1. Mit globalen Variablen, die einfach zu implementieren, sondern erhöht die Komplexität und gegenseitige Abhängigkeiten. Sie können einige knifflige Lösungen here und here finden.
  2. Multithreading, die versucht, die Interaktionen der Verarbeitungsabschnitte und der Benutzeroberfläche in separaten Threads auszuführen. Es wird robuster sein (wenn Sie Erfahrung in der Arbeit mit Threads haben), aber mehr Code benötigt. Es gibt eine detaillierte Implementierungen für diese here.

Meine Lösung basiert auf der Verwendung von globalen Variablen. Es ist wirklich wie die Lösungen, die ich bereits verknüpft haben, aber beide von ihnen versuchen, Start/Stopp-Tasten zu implementieren, während ich gleichzeitig einen neuen aktuellen Prozess und starten Sie müssen aufhören:

function TestUI 
clc; clear variables; close all; 
figure; axis equal; hold on; 
xlim([0 100]); ylim([0 100]); 
set(gca, 'ButtonDownFcn', @AxisButtonDownFcn); 
global AnimateIsRunning 
AnimateIsRunning = false; 
end 

function AxisButtonDownFcn(ah, ~) 
C = get(gca,'CurrentPoint'); 
global xnow ynow AnimateIsRunning 
xnow = C(1, 1); ynow = C(1, 2); 
if ~AnimateIsRunning 
    Animate(ah); 
end; 
end 

function Animate(ah) 
T = -pi:0.02:pi; r = 5; 
global xnow ynow AnimateIsRunning 
AnimateIsRunning = true; 
x = -1; y = -1; 
while ~((x==xnow)&&(y==ynow)) 
    x = xnow; y = ynow; 
    for t = T 
     if ~((xnow==x)&&(ynow==y)) 
      break; 
     end; 
     if ishandle(ah) 
      ph = plot(ah, x+r*cos(t), y+r*sin(t), '.'); 
      drawnow; 
      if ishandle(ph) 
       delete(ph) 
      end 
     end 
    end 
end; 
AnimateIsRunning = false; 
end 

es einfach verhindern, dass Animate zweimal aufgerufen wird. Es ruft Animate auf, wenn es nicht läuft, andernfalls informiert es gerade laufende Animate, dass es neue Anfrage gibt.