Qt QWidget对象的Eventable接口(Scala实现)是本文将要介绍的内容,Scala编程语言近来抓住了很多开发者的眼球。如果你粗略浏览Scala的网站,你会觉得Scala是一种纯粹的面向对象编程语言,而又无缝地结合了命令式和函数式的编程风格。
这个Eventable接口是我项目中常用的一个东西,scala强调FP,但是Qt Jambi本身是基于OOP的,事件重载需要在类里面进行。
在这个例子中,( http://my.oschina.net/janpoem/blog/7017 )大家可以看到经常会这样展开一个类去重载:
- 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("双击了!")
- }
- )
小结:详解 Qt QWidget对象的Eventable接口(Scala实现)的内容介绍完了,希望本文对你有帮助,更毒内容请参考编辑推荐!
【编辑推荐】