2016-11-28 1 views
0

J'ai un groupe OptionGroup avec mutliSelect (true) (cases à cocher) dans mon application VAADIN. Lorsque je clique sur le champ case à cocher, une logique de calcul lourde est exécutée dans l'écouteur valueChange. Je voudrais empêcher l'interaction de l'utilisateur lorsque cette logique est exécutée. Je veux donc afficher un ProgressBar avec un indicateur indéterminé défini sur true à l'aide de la méthode d'interrogation.VAADIN - envoi de réponse à partir d'une logique de calcul lourde

La logique utilisée pour l'auditeur ValueChange se présente comme suit

@Override 
public void valueChange(Property.ValueChangeEvent event) { 
    Thread valueChangeProcessorThread = new Thread(new ValueChangeProcessor(myComponent)); 
    valueChangeProcessorThread.start(); 
} 

Comme vous pouvez le voir, un fil est exécuté qui est défini comme suit

private class ValueChangeProcessor implements Runnable { 
    private final MyComponent myComponent; 

    public ValueChangeProcessor(MyComponent myComponent) { 
     this.myComponent = myComponent; 
    } 

    public synchronized MyComponent getMyComponent() { 
     return myComponent; 
    } 

    @Override 
    public void run() { 
     getMyComponent().getUI().access(new Runnable() { 
      @Override 
      public void run() { 
       showWaitIndicator(); 
      } 
     }); 

     // Some heavy computation logic 

     hideWaitIndicator(); 
    } 
} 

J'utilise ici UI.access() pour affiche un ProgessBar indéterminé. La logique pour le faire est disponible dans la méthode showWaitIndicator(). Lorsque le calcul est terminé, je masque le ProgressBar en appelant la méthode hideWaitIndicator().

Ici, il arrive que parfois la méthode showWaitIndicator() soit exécutée seulement après que la méthode de calcul et hideWaitIndicator() soit exécutée. Comment puis-je m'assurer que tout est exécuté dans l'ordre voulu?

Répondre

0

Je me suis juste battu avec le même problème pendant le week-end. Voici ma mise en œuvre actuelle.

Le point principal est que j'utilise l'écouteur attach pour démarrer le thread d'arrière-plan.

Notez également que cette approche nécessite vaadin push pour être activée afin de fonctionner.

Utilisation:

VModalTaskDialog.execute(dialog -> { 
    dialog.updateProgressIndeterminate(false); 
    dialog.updateCancelable(true); 
    dialog.updateTitle("Processing..."); 

    // simulate work 
    for(int i = 0; i < 10; i++) 
    { 
    if(dialog.isCanceled()) 
    { 
     System.out.println("canceled"); 
     return; 
    } 

    Thread.sleep(1000); 
    dialog.updateDetails(i + " Details Details Details Details Details Details Details Details Details Details Details Details."); 
    dialog.updateProgress(0.1f + i * 0.1f); 
    } 

    UI.getCurrent().access(() -> { 
    // update UI from background task 
    }); 
}); 

Le code:

public class VModalTaskDialog 
{ 
    @SuppressWarnings("unused") 
    private static final Logger LOGGER = Logger.getLogger(VModalTaskDialog.class.getName()); 

    public static interface IModalTask 
    { 
    public void execute(VModalTaskDialog dialog) throws Exception; 
    } 

    public static void execute(IModalTask modalTask) 
    { 
    execute(500, 300, modalTask); 
    } 

    /** 
    * dimensions required for centering on screen 
    */ 
    public static void execute(int width, int height, IModalTask modalTask /* , Executable callback */) 
    { 
    VModalTaskDialog modalTaskDialog = new VModalTaskDialog(); 

    modalTaskDialog.m_modalTask = modalTask; 

    modalTaskDialog.initAndStart(width, height); 
    } 

    private IModalTask m_modalTask  = null; 
    private Thread  m_modalTaskThread = null; 

    private Window  m_window   = null; 
    private Label  m_statusLabel  = null; 
    private ProgressBar m_progressBar  = null; 
    private Label  m_detailsLabel = null; 

    private Button  m_cancelButton = null; 
    private boolean  m_cancelable  = false; 
    private boolean  m_canceled  = false; 

    public VModalTaskDialog() 
    { 
    // nothing 
    } 

    private void initAndStart(int width, int height) 
    { 
    m_window = new Window(); 

    m_window.setModal(true); 

    m_window.addCloseListener(e -> tryCancel()); 

    m_window.setWidth(width + "px"); 
    m_window.setHeight(height + "px"); 

    VerticalLayout contentLayout = new VerticalLayout(); 
    contentLayout.setSizeFull(); 

    { 
     // show scroll bars if status or details overflow the available space 
     Panel panel = new Panel(); 
     panel.setSizeFull(); 
     panel.setStyleName("borderless"); 

     { 
     VerticalLayout layoutInPanel1 = new VerticalLayout(); 
     layoutInPanel1.setWidth("100%"); 

     // for some reason, the first vertical layout child of a panel creates a narrow margin 
     // the second nested layout creates normal width margin 
     VerticalLayout layoutInPanel2 = new VerticalLayout(); 
     layoutInPanel2.setWidth("100%"); 
     layoutInPanel2.setSpacing(true); 
     layoutInPanel2.setMargin(true); 

     { 
      m_statusLabel = new Label(); 
      m_statusLabel.setStyleName("h3 no-margin"); 
      m_statusLabel.setWidth("100%"); // get label to wrap text 
      m_statusLabel.setContentMode(ContentMode.HTML); 
      layoutInPanel2.addComponent(m_statusLabel); 
     } 
     { 
      m_progressBar = new ProgressBar(); 
      m_progressBar.setIndeterminate(true); 
      m_progressBar.setStyleName("vaadin-modal-task-progress"); 
      m_progressBar.setWidth("100%"); 
      layoutInPanel2.addComponent(m_progressBar); 
     } 
     { 
      m_detailsLabel = new Label(); 
      m_detailsLabel.setStyleName("vaadin-modal-task-details"); 
      m_detailsLabel.setWidth("100%"); // get label to wrap text 
      m_detailsLabel.setContentMode(ContentMode.HTML); 
      layoutInPanel2.addComponent(m_detailsLabel); 
     } 

     layoutInPanel1.addComponent(layoutInPanel2); 
     panel.setContent(layoutInPanel1); 
     } 

     contentLayout.addComponent(panel); 
     contentLayout.setExpandRatio(panel, 1f); 
    } 

    { 

     HorizontalLayout buttonRowLayout = new HorizontalLayout(); 
     buttonRowLayout.setMargin(new MarginInfo(false, false, true, false)); 

     { 
     m_cancelButton = new Button("Cancel"); 
     m_cancelButton.setEnabled(false); 
     m_cancelButton.addClickListener(e -> tryCancel()); 

     buttonRowLayout.addComponent(m_cancelButton); 
     } 

     contentLayout.addComponent(buttonRowLayout); 
     contentLayout.setComponentAlignment(buttonRowLayout, Alignment.BOTTOM_CENTER); 
    } 

    m_window.setContent(contentLayout); 

    m_window.center(); 

    m_window.addAttachListener(e -> eventWindowAttached()); 

    // show dialog 
    UI.getCurrent().addWindow(m_window); 
    } 

    private void eventWindowAttached() 
    { 
    // LOGGER.log(Level.INFO, "modal task window attached, starting background thread"); 

    // start modal task in background 
    m_modalTaskThread = new Thread(new Runnable() { 
     @Override 
     public void run() 
     { 
     runInThread(); 
     } 
    }); 
    m_modalTaskThread.setName("modal-task-" + new Random().nextInt(Integer.MAX_VALUE)); 
    m_modalTaskThread.start(); 

    // waiting until modalTaskAction finishes 
    } 

    private void runInThread() 
    { 
    // LOGGER.log(Level.INFO, "running modal task in thread"); 

    Throwable throwableFromTask = null; 

    try 
    { 
     m_modalTask.execute(VModalTaskDialog.this); 
    } 
    catch(InterruptedException | InterruptedIOException e) 
    { 
     if(m_canceled) 
     { 
     //  LOGGER.log(Level.INFO, "canceled: " + t); 
     // expected 
     } 
     else 
     { 
     // interruption without cancellation is unexpected 
     throwableFromTask = e; 
     } 
    } 
    catch(Throwable t) 
    { 
     // task failed 
     throwableFromTask = t; 
    } 

    // close dialog 
    safeClose(); 

    // maybe show exception 
    try 
    { 
     if(throwableFromTask != null) 
     { 
     final Throwable finalThrowableFromTask = throwableFromTask; 
     UI.getCurrent().access(() -> Notification.show("" + finalThrowableFromTask, Type.ERROR_MESSAGE)); 
     } 
    } 
    catch(Throwable t) 
    { 
     LOGGER.log(Level.WARNING, "failed to show modal task exception: " + t); 
    } 
    } 

    private void safeClose() 
    { 
    try 
    { 
     //  LOGGER.log(Level.INFO, "closing modal task dialog"); 
     UI.getCurrent().access(() -> m_window.close()); 
    } 
    catch(Throwable t) 
    { 
     LOGGER.log(Level.WARNING, "failed to close modal task dialog: " + t); 
    } 
    } 

    /** update method, to be called from task thread */ 
    public void updateThreadName(String threadName) 
    { 
    m_modalTaskThread.setName(String.valueOf(threadName)); 
    } 

    /** update method, to be called from task thread */ 
    public void updateThreadPriority(int priority) 
    { 
    m_modalTaskThread.setPriority(priority); 
    } 

    /** update method, to be called from task thread */ 
    public void updateCancelable(boolean cancelable) 
    { 
    m_cancelable = cancelable; 

    UI.getCurrent().access(() -> m_cancelButton.setEnabled(cancelable)); 
    } 

    /** update method, to be called from task thread */ 
    public void updateTitle(String title) 
    { 
    UI.getCurrent().access(() -> m_statusLabel.setValue(title)); 
    } 

    /** update method, to be called from task thread */ 
    public void updateDetails(String details) 
    { 
    UI.getCurrent().access(() -> m_detailsLabel.setValue(details)); 
    } 

    /** update method, to be called from task thread */ 
    public void updateProgress(float progress) 
    { 
    UI.getCurrent().access(() -> m_progressBar.setValue(Float.valueOf(progress))); 
    } 

    /** update method, to be called from task thread */ 
    public void updateProgressIndeterminate(boolean indeterminate) 
    { 
    UI.getCurrent().access(() -> m_progressBar.setIndeterminate(indeterminate)); 
    } 

    private void tryCancel() 
    { 
    if(!m_cancelable) 
    { 
     return; 
    } 

    m_canceled = true; 

    // LOGGER.log(Level.INFO, "cancel: interrupting modal task thread"); 
    m_modalTaskThread.interrupt(); 
    } 

    public boolean isCanceled() 
    { 
    return m_canceled; 
    } 
} 

Et il ressemble à ceci:

dialog

+0

Merci de partager votre solution. Mais je suis toujours à la recherche d'aide pour résoudre le problème avec la logique de ma solution. – shaunthomas999

+0

Utilisez la même logique que dans mon code: Dans votre méthode d'écoute de changement de valeur, ajoutez d'abord un écouteur d'attachement à votre indicateur d'attente, puis affichez votre indicateur d'attente. Ensuite, dans le gestionnaire d'attachement, démarrez votre thread d'arrière-plan. – Zalumon