歡迎您光臨本站 註冊首頁

用Scala實現Qt QWidget對象的Eventable介面

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
今天我們來講講用Scala實現Qt QWidget對象的Eventable介面.這個Eventable介面是我項目中常用的一個東西,Scala強調FP,但是Qt Jambi本身是基於OOP的,事件重載需要在類裡面進行.在前面展示的例子中,大家可以看到經常會這樣展開一個類去重載:
new QLabel {
override def xxxxEvent } 這種聲明的方法多了其實很容易讓人覺得不規範,而且閱讀也是不易.我萌生了讓將js那種聲明事件風格的代碼加入至此,js是一個可以很fp的語言,而scala也是,這不是一個很好的決定嗎?獻上具體的代碼:
package yourporject.package
import scala.collection.mutable.{ ArrayBuffer, HashMap } import com.trolltech.qt.gui._ import com.trolltech.qt.core._ import com.trolltech.qt.core.QEvent import com.trolltech.qt.QSignalEmitter._ import com.agiers.mvc.Base /*
* Base類裡面封裝了的是對於Java和Scala既有類的方法擴展,使用的是隱式混入的方式,不會改變對象本身.
* 如
* "onClick".toEventName => click
* "中文字".encode => url encode
* "繁體字".encodeSys => 這個是根據客戶端操作系統默認的字元編碼進行urlencode
* "繁體字".toSimplified => 繁體轉簡體
* "簡體字".toTraditional => 簡體轉繁體
* "hello_world".toCamelCase => HelloWorld
* "good guys".dump("temp.txt") => 將字元串內容輸入到一個io文件中
* "hello world".md5 => 將字元串md5加密
*/
trait Eventable[T <: QWidget] extends QWidget with Base {
// 定義閉包的格式聲明
// 凡是在Eventable里使用閉包的類型,應該使用Fn類型
// 修改閉包類型,應該在此修改,而不在具體聲明的地方修改
type Fn = EventHandle => Unit
// 定義一個event的類型組合
// 這個代表的實際上是String -> Fn或者(String, Fn)
type Ev = (String, Fn)
/**
* 事件接管對象
* 用於接管聲明事件時的閉包處理,並臨時寄存該閉包中的各種狀態和變數
* @TODO 要逐漸增加他的寄存和讀取的介面
* @author Janpoem
*/
sealed case class EventHandle(val widget : T, val event : QEvent) {
// 這個是用來獲取該widget執行event時的狀態的
private var _break = false
// 以下
def isBreak = _break
def isBreak_=(is : Boolean) = _break = is
def break(fn : EventHandle => Boolean) = isBreak = fn(this)
}
/**
* 閉包的存放容器
* 允許將閉包作為一個隊列存放,並在fire的時,按照隊列先後順序執行.
* @author Janpoem
*/
sealed case class FnContainer(fn : Fn) {
private var fns = ArrayBuffer[Fn](fn)


def (fn : Fn) : this.type = {
fns = fn
this
}
def fire(widget : T, event : QEvent) : EventHandle = {
val handle = EventHandle(widget, event)
fns.foreach(_(handle))
handle
}
}
// 定義Qt標準時間類型轉換到當前類的助記名
// name統一使用小寫
// @TODO 要不斷增加QEvent.Type的內容
private val _eventsMap = HashMap[QEvent.Type, String](
QEvent.Type.Show
-> "show",
QEvent.Type.MouseButtonPress
-> "click",
QEvent.Type.MouseButtonDblClick -> "doubleclick",
QEvent.Type.FocusIn
-> "focus",
QEvent.Type.FocusOut
-> "blur",
QEvent.Type.Enter
-> "enter",
QEvent.Type.Leave
-> "leave"

// 事件
private val _events = HashMap[String, FnContainer]()
// 傳入Qt的QEvent.Type,獲取其在Eventable內部的快捷助記名
def eventType2Name(_type : QEvent.Type) : Option[String] = _eventsMap.get(_type)
// 裝載事件
// w.addEvent("show", handle => { /* */ })
def addEvent(s : String, fn : Fn) : this.type = {
val name = s.toEventName
if (!this.hasEvent(name))
_events(name) = FnContainer(fn)
else
_events(name) fn
this
}
// w.addEvent("click" -> { handle => println(handle.event) })
def addEvent(event : Ev) : thisthis.type = this.addEvent(event._1, event._2)
def addEvents(events : Ev*) : this.type = {
events.foreach(this.addEvent(_))
this
}
// 判斷是否存在事件
def hasEvent(name : String) : Boolean = _events.contains(name.toEventName)
// Qt事件覆蓋
override def event(event : QEvent) : Boolean = {
eventType2Name(event.`type`()) match {
case Some(name) =>
if (this.hasEvent(name)) {
val handle = _events(name).fire(this.asInstanceOf[T], event)
}
case _ =>
}
super.event(event)
}
}
這個Eventable只是一個很初步的封裝,只是針對所有的QWidget適用,我還有好些想法,比如延時事件激活,定時事件循環.並且希望能對QObject進行全部的適用,而對於Qt的信號槽,自然也要兼容.唉,想法太多,可惜時間太有限.先用著吧,能好像寫js一樣寫事件聲明,該知足了.
下面奉上使用的代碼:
class Widget extends QWidget with Eventable[QWidget]
val w = new Widget() w.addEvent("onClick", handle => {
println("單擊了!") }) w.addEvents(
"show" -> { handle =>
println("窗口顯示了")


},
"doubleClick" -> { handle =>
println("雙擊了!")
} )


[火星人 ] 用Scala實現Qt QWidget對象的Eventable介面已經有713次圍觀

http://coctec.com/docs/java/show-post-60036.html