Swing

2013-03-04 16 views
5
ile Asynchronous UI güncelleştirmesi

Bir UI (ayrıca Swing'de) aracılığıyla tetiklenmesini istediğim bir Scala programı yazdım. Sorun, tetiklediğimde, UI arka plan programı tamamlanana kadar askıda kalıyor. Bunun üstesinden gelmenin tek yolunun, programın başka bir iş parçacığı/aktörde çalışıp, kullanıcı arabirimini gerektiği gibi güncellemesini sağladığını düşünmek zorundayım. Güncelleme, şu anda işlenmekte olan dosyayı ve bir ilerleme çubuğunu gösteren bir durum çubuğu içerecektir.Swing

Scala oyuncuları kullanımdan kaldırıldığı için, bir tür temel çoklu iş parçacığı çalıştırmak için Akka'yı geçmeye çalışırken zor bir zaman geçiriyorum. Akka web sitesinde verilen örnekler de oldukça karmaşıktır.

Fakat bundan daha fazlası, kafamı bu soruna nasıl gireceğimi anlatmak zor. Ne kadar gelebilir geçerli:

  1. Arkaplan programı
  2. UI

Adım 3 olan bir şeyi güncellemek için UI söyler başka aktör var ana programlarından biri bir aktör olarak çalışır Beni ne karıştırıyor? Arabirimdeki bazı değişkenleri kilitlemeden UI'ye nasıl söyleyebilirim?

Ayrıca, bu sorunun daha önce çözüldüğünden eminim. Aynısı için herhangi bir örnek kod çok takdir edilecektir.

=======================

DÜZENLEME: Aktörler kullanmanın

yerine benim için çalıştı Ne, benzer Futures kullanılan yol pagoda_5b onun cevabını açıkladı:

Taşı durum çubuğu ve def top dışına ilerleme çubuğu ve yapmak onları küresel değişkenler:

object MyGUIProject extends SimpleSwingApplication { 

    val txtStatus = new Label //Global 
    val progressBar = new ProgressBar //Global 

    def top = new MainFrame { 
    ... 
    } 
} 

Çağrı ı cl bir gelecekte arka plan işlemi Eğer yeni bir Konu uzun görevi çalıştırmak ve sadece emin yapabilir basit bir şey gerekiyorsa

def updateStatus(txt: String) = { 
    val fut = future { 
     Swing.onEDT { 
     txtStatus.text = txt 
     } 
    } 
} 

def doLongBackgroundProcess(str:String) = { 
    val taskList = getTasks(str) 
    taskList foreach {x=> 
     updateStatus("Currently doing task: " + x) 
     //Start doing it 
    } 
} 
+0

Scala programınızı UI'den başlatmak için programınızın EDT'de çalışmadığından emin olun. SwingWorker, bu konudaki çalışmayı kolaylaştırabilir veya 'Executors' kullanabilirsiniz. "Swing'de Eşzamanlılık" bölümüne bakın: http://stackoverflow.com/tags/swing/info –

+0

Son derece ilgili http://stackoverflow.com/questions/13516752/using-opengl-with-akka-actors-guaranteeing-a- tekil-thread-is-a-specific-için- –

+0

Bkz. ['SwingWorker çalışıyor '] (http://stackoverflow.com/a/11546203/597657). –

cevap

7

scala 2.10 için

scala.concurrent.future'u kullanabilir ve ardından tamamlandığında bir geri arama kaydedebilirsiniz. Geri arama, EDT iş parçacığındaki GUI'yi güncelleyecektir.

Bunu yapalım!

//in your swing gui event listener (e.g. button clicked, combo selected, ...) 
import scala.concurrent.future 
//needed to execute futures on a default implicit context 
import scala.concurrent.ExecutionContext.Implicits._ 


val backgroundOperation: Future[Result] = future { 
    //... do that thing, on another thread 
    theResult 
} 

//this goes on without blocking 
backgroundOperation onSuccess { 
    case result => Swing.onEDT { 
     //do your GUI update here 
    } 
} 

Bu en basit durumdur:

  1. biz sadece başarılı vaka

hallediyoruz
  • hiçbir ilerleme ile, yapıldığında sadece güncelliyoruz

    (1) ile başa çıkmak için map/flatMap meth kullanarak farklı vadeli işlemleri birleştirebilirsiniz. Future örneğindeki ods. Eğer

    //example progress update 
    val backgroundCombination = backgroundOperation map { partial: Result => 
        progress(2) 
        //process the partial result and obtain 
        myResult2 
    } //here you can map again and again 
    
    def progress(step: Int) { 
        Swing.onEDT { 
         //do your GUI progress update here 
        } 
    } 
    

    (2) Bir geri arama onFailure kayıt ya da her ikisi davaları baş edebilmek için bir Swing.onEDT bloğunda bunu o çağrılan gibi, UI ilerleme güncelleyebilirsiniz (her zaman yapma emin kullanmak isterseniz scaladocs ve ilgili SIP (SIP örnekleri modası geçmiş görünüyor olsa da, onlar size vermelidir iyi bir fikir)

  • +0

    Detaylı kod için teşekkürler! Eve geldiğimde tonit'i deneyeceğim ve nasıl gittiğini anlatayım –

    +0

    Lütfen herhangi bir sorun veya sorunla karşılaşırsanız bize bildirin ve –

    +0

    Geç cevap için özür dileriz. Vadeli işlemler benim için mükemmel çalışıyor, hatta var olduklarını bilmiyordum! Anlamak için biraz zaman harcadım, ama sonunda cevabın sayesinde benim için çalıştı. Ben çok benzer bir şekilde, gelecekteki arka plan sürecini çağırdım, gerekli değişkenleri global değişkenlere taşıdım ve Swing.EDT –

    1

    :

    val btnStart = new Button { 
         text = "Click me to start" 
         reactions += { 
         case ButtonClicked(_) => { 
          val myFuture = future { 
          doLongBackgroundProcess(someString) 
          } 
    
          myFuture onSuccess { 
           case _ => Swing.onEDT { 
            txtStatus.text = "Done" 
           } 
          } 
         } 
        } 
    } 
    

    bir future ve Swing.onEDT kullanarak arka plan sürecinde onları güncelleyin: butonuna Tran EDT güncellemek için:

    def swing(task: => Unit) = SwingUtilities.invokeLater(new Runnable { 
        def run() { task } 
        }) 
        def thread(task: => Unit) = new Thread(new Runnable { 
        def run() {task} 
        }).run() 
    
        thread({ 
        val stuff = longRunningTask() 
        swing(updateGui(stuff)) 
        }) 
    
    +0

    İlginç bir yaklaşım kullanmayı denemek zorundayım.Sadece Aktörler hakkında düşünüyordum, ben basit iplikler kullanmayı tamamen unutmuşum –

    +0

    Gerçekten oyunculara ihtiyaç duymadıkça, KISS –

    +0

    'u takip etmeliydim Bende 'thread' işlevi 'run() 'yerine' start()' olmalıdır. iş parçacığı kullanımının hangi yenilgileri engellediğini gösteriyor gibi görünüyor. – monnef

    4

    . onComplete

    ilgili örnekler için

    ile Aktörler, aşağıdakiler sizin için işe yarayabilir.

    iki aktör vardır: veri işlemleri yapar

    • WorkerActor (burada Thread.sleep basit döngü vardır).
    • GUIUpdateActor -

    arayüzü güncellemesi yöntemi handleGuiProgressEvent güncelleme olayını aldığında yöntem handleGuiProgressEvent arayarak ilerleme ve güncellemeleri kullanıcı arayüzü hakkında güncellemeler alır: Bu aktör başka aktöre işin ilerlemesiyle ilgili mesajları gönderir. Önemli bir nokta, bu yöntemin Akka iş parçacıklarından birini kullanarak Aktör tarafından çağrılması ve Swing olayı gönderme iş parçacığında Swing çalışması yapmak için Swing.onEDT komutunu kullanmasıdır.

    Geçerli iş parçacığının ne olduğunu görmek için çeşitli yerlere aşağıdakileri ekleyebilirsiniz.

    Kod çalıştırılabilir Swing/Akka uygulamasıdır.

    import akka.actor.{Props, ActorRef, Actor, ActorSystem} 
    import swing._ 
    import event.ButtonClicked 
    
    trait GUIProgressEventHandler { 
        def handleGuiProgressEvent(event: GuiEvent) 
    } 
    
    abstract class GuiEvent 
    
    case class GuiProgressEvent(val percentage: Int) extends GuiEvent 
    object ProcessingFinished extends GuiEvent 
    
    
    object SwingAkkaGUI extends SimpleSwingApplication with GUIProgressEventHandler { 
    
        lazy val processItButton = new Button {text = "Process it"} 
        lazy val progressBar = new ProgressBar() {min = 0; max = 100} 
    
        def top = new MainFrame { 
        title = "Swing GUI with Akka actors" 
    
        contents = new BoxPanel(Orientation.Horizontal) { 
         contents += processItButton 
         contents += progressBar 
         contents += new CheckBox(text = "another GUI element") 
        } 
    
        val workerActor = createActorSystemWithWorkerActor() 
    
        listenTo(processItButton) 
    
        reactions += { 
         case ButtonClicked(b) => { 
         processItButton.enabled = false 
         processItButton.text = "Processing" 
         workerActor ! "Start" 
         } 
    
        } 
    
        } 
    
        def handleGuiProgressEvent(event: GuiEvent) { 
        event match { 
         case progress: GuiProgressEvent => Swing.onEDT{ 
         progressBar.value = progress.percentage 
         } 
         case ProcessingFinished => Swing.onEDT{ 
         processItButton.text = "Process it" 
         processItButton.enabled = true 
         } 
        } 
    
        } 
    
        def createActorSystemWithWorkerActor():ActorRef = { 
        def system = ActorSystem("ActorSystem") 
    
        val guiUpdateActor = system.actorOf(
         Props[GUIUpdateActor].withCreator(new GUIUpdateActor(this)), name = "guiUpdateActor") 
    
        val workerActor = system.actorOf(
         Props[WorkerActor].withCreator(new WorkerActor(guiUpdateActor)), name = "workerActor") 
    
        workerActor 
        } 
    
    
        class GUIUpdateActor(val gui:GUIProgressEventHandler) extends Actor { 
        def receive = { 
         case event: GuiEvent => gui.handleGuiProgressEvent(event) 
        } 
        } 
    
    
        class WorkerActor(val guiUpdateActor: ActorRef) extends Actor { 
        def receive = { 
         case "Start" => { 
         for (percentDone <- 0 to 100) { 
          Thread.sleep(50) 
          guiUpdateActor ! GuiProgressEvent(percentDone) 
         } 
         } 
         guiUpdateActor ! ProcessingFinished 
        } 
        } 
    
    } 
    
    +2

    kullanarak arka plan işlemi aracılığıyla bunları güncellemek için kullanılan vadeli işlemler. Akka ve aktörler ile yeni bir kullanıcı için belgelerin eksikliğinden (veya fazlalığından) dolayı ve aynı zamanda API'nın farklı sürümleri olduğundan dolayı bir şey yapmak çok zordur. Şimdilik, Futures'ın istediğim gibi yaptıklarına inanıyorum. Ben şimdi aktörler bir UI –

    +0

    Nice bir overkill biraz hissediyorum, teşekkür ederim. FYI, 'systems' (her ikisi de system.actorOf() '(akka sürümünün her çağrısı için ilk argüman olarak" Props (classOf [GuiUpdateActor], bu) "ve" Props (classOf [WorkerActor], guiUpdateActor) "kullanmak zorunda kaldı 2.3-M2). –