2011-01-06 9 views
9

Tôi đang viết một ứng dụng nhỏ trong scala. Ứng dụng xử lý các tệp nhật ký đơn giản. Bởi vì việc xử lý mất một thời gian, tôi đã quyết định để cho lõi ứng dụng của tôi mở rộng Diễn viên.Tương tác với các diễn viên trong các ứng dụng scala swing

class Application extends Actor { 
    def react() { 
    loop { 
     react { 
     case Process(file) => // do something interesting with file... 
     } 
    } 
    } 
} 

Việc xử lý tệp nhật ký được kích hoạt bằng cách nhấp vào nút trong gui. Các gui sử dụng scala swing.

object Gui extends SimpleSwingApplication { 
    val application = new Application().start() 

    def top = new MainFrame { 
    val startButton = new Button 

    reactions += { 
     case ButtonClicked(`startButton`) => application ! Process(file) 
    } 
    } 
} 

Bây giờ, lõi ứng dụng cần thông báo cho gui về tiến trình hiện tại.

sender ! Progress(value) // whenever progress is made 

Tôi đã giải quyết vấn đề này bằng cách tạo một diễn viên riêng bên trong gui. Các diễn viên được thực hiện bên trong chủ đề edt. Nó lắng nghe thông điệp từ lõi ứng dụng và cập nhật gui.

object Gui extends SimpleSwingApplication { 
    val actor = new Actor { 
     override val scheduler = new SchedulerAdapter { 
     def execute(fun: => Unit) { Swing.onEDT(fun) } 
     } 
     start() 

     def act() { 
     loop { 
      react { 
      case ForwardToApplication(message) => application ! message 
      case Progress(value) => progressBar.value = value 
      } 
     } 
     } 
    } 
    } 

Kể từ khi lõi ứng dụng cần phải biết về người gửi tin nhắn, tôi cũng sử dụng diễn viên này để chuyển tiếp các tin nhắn từ gui cho lõi đơn, làm diễn viên của tôi người gửi mới.

reactions += { 
    case ButtonClicked(`startButton`) => actor ! ForwardToApplication(Process(file)) 
    } 

Mã này chỉ hoạt động tốt. Câu hỏi của tôi: Có cách nào đơn giản hơn để làm điều này không? Thật tuyệt vời khi sử dụng đơn giản cơ chế phản ứng cho thông điệp ứng dụng của tôi:

reactions += { 
    case Progress(value) => progressBar.value = value 
    } 

Bất kỳ ý tưởng nào để đạt được điều này?

Trả lời

6

tôi đã mở rộng trên gerferras ý tưởng thực hiện ứng dụng của tôi một swing.Publisher. Lớp sau đóng vai trò trung gian giữa một số swing.Reactor và một số Actor.

import actors.Actor 
import swing.Publisher 
import swing.event.Event 
import swing.Swing.onEDT 

case class Send(event: Any)(implicit intermediator: Intermediator) { 
    intermediator ! this 
} 
case class Receive(event: Any) extends Event 

case class Intermediator(application: Actor) extends Actor with Publisher { 
    start() 

    def act() { 
    loop { 
     react { 
     case Send(evt) => application ! evt 
     case evt => onEDT(publish(Receive(evt))) 
     } 
    } 
    } 
} 

Bây giờ phản ứng của tôi có thể bao gồm cả sự kiện và sự kiện ứng dụng.

implicit val intermediator = Intermediator(application) 
listenTo(intermediator, button) 

reactions += { 
    case ButtonClicked(`button`) => Send(Process(file)) 
    case Receive(Progress(value)) => progressBar.value = value 
} 

Lưu ý cách case class Send cung cấp một số đường cú pháp để dễ dàng tạo ra các sự kiện và truyền cho những người đứng giữa để điều đình.

+0

Tôi nghĩ đó là một giải pháp tốt. Có lẽ tôi xứng đáng nhận được một upvote trong trở lại;) – gerferra

+0

Tôi nghĩ rằng bạn đang thiếu 'trung gian!' Trong phản ứng của bạn với 'ButtonClicked' – gerferra

+0

@geferra Cuộc gọi' trung gian 'bên trong hàm tạo 'trường hợp gửi Send'. 'Trung gian' được truyền qua một tham số ngầm định. Tôi đang upvoting câu trả lời của bạn vì nó cung cấp nguồn cảm hứng cho giải pháp của riêng tôi. –

4

Có thể điều này đơn giản hơn nhưng không biết nếu nó tốt hơn. Thay vì làm cho ứng dụng của bạn phụ trợ một diễn viên, bạn có thể tạo một diễn viên vô danh mỗi khi bạn cần để xử lý các file:

reactions += { 
    case ButtonClicked(`startButton`) => application.process(file, { v: Int => Swing.onEDT(progressBar.value = v) }) 
} 

Đối với phần cập nhật tiến độ, bạn có thể vượt qua một callback với phương pháp quá trình được thực hiện mỗi Hiện một tiến trình mới được thực hiện:

import scala.actors.Actor.actor 

def process(f: File, progress: Int => Unit) { 
    actor { 
    // process file while notifying the progress using the callback 
    progress(n) 
    } 
} 

Ngoài ra (chưa được kiểm tra) bạn có thể làm cho ứng dụng của bạn một scala.swing.Publisher và, thay vì sử dụng gọi lại, xuất bản và sự kiện mỗi lần. Vì vậy, các mã có thể là:

listenTo(startButton, application) //application is a Publisher 

reactions += { 
    case ButtonClicked(`startButton`) => application.process(file) 
    case Progress(v) => progressBar.value = v 
} 

Và trong ứng dụng:

import scala.actors.Actor.actor 

def process(f: File) { 
    actor { 
    // process file while notifying the progress using an scala.swing.event.Event 
    publish(Progess(n)) 
    } 
} 
+0

Ý tưởng đầu tiên của bạn với các tác phẩm gọi lại, nhưng tôi nghĩ rằng cách tiếp cận của tôi với diễn viên có thể mở rộng hơn Nếu tôi cần truyền các thông báo bổ sung. –

+0

Còn cách tiếp cận thứ hai thì sao?Ứng dụng của bạn sẽ phần nào được gắn với giao diện Swing nhưng tôi nghĩ nó không tệ và nếu tôi đọc chính xác câu hỏi của bạn, đó là những gì bạn đang yêu cầu vào cuối – gerferra

+0

Tôi đã thử cách tiếp cận thứ hai của bạn và phần nào mở rộng nó. Xem câu trả lời của riêng tôi như thế nào giải pháp này trông như thế nào. –