core java 梗概_Qt in Scala(JVM)开发梗概

本文将讲述Qt in Scala(JVM)开发梗概。

前言

一直有人问Qt的开发情况,希望有个感性的认识。一直也有整理这方面资料的冲动,但时间也很紧迫,所以长话短说。目前将目标锁定Qt Jambi版本吧,熟悉了Qt以后,我对其C++版本的兴趣也十分浓厚,所以,将来再慢慢整理吧。

本篇文章涉及的开发环境如下:windows xp sp2,JDK6u21,Scala 2.8.0 final,Qt Jambi LGPL 4.5.2_01,IDE选NetBeans吧!

列出Qt的文档中心的几个重要资源的链接入口:

◆CSS样式说明(内容彼此交叉,十分详尽,你只需要有一点点的CSS基础就能明白)

关于Qt的点滴,我会以注释的形式写在代码里面,毕竟都是写代码的,对注释会比较敏感。

Hello Qt in Scala

package qt.demo

import com.trolltech.qt.gui._

object HelloQt {

def main(args: Array[String]):Unit= {

QApplication.initialize(args)

(new QLabel("Hello Qt")).show

QApplication.exec

}

}

可能上述代码还能再度简化一下,比如去掉new QLabel两边的括号(但可能会很怪异了)。这个很简单吧,输出结果如下图:

05d8a2eab5a80760c3a2996df9f8cb53.png

QApplication是Qt的一个全局单例类,就把他看作是一个总控制中心吧。他是一个static类,通过调用QApplication.instance()方法,可获得当前运行过程中的app实例。

QApplication.instance是一个全局控制实例,这里所定义的内容(可以定义的东西,详细请看手册),除非在实例具体某个对象时有具体设置,否则全局都按照instance的设置进行。当然,其实多数时候,我会用他来控制全局的样式定义。

好吧,上述的例子实在简单的有些恶心了,我们来些实际一点的东西:

package qt.demo

import com.trolltech.qt.gui._

import com.trolltech.qt.core.Qt._

object CustomWindow {

valglobalStyle="""

* { font-family: Mircosoft Yahei; font-size: 12px; color: #333; }

#mainWindow { border: 40px solid #ccc; border-image: url(classpath:qt/demo/resource/window.png) 40 stretch; }

"""

def main(args: Array[String]):Unit= {

QApplication.initialize(args)

QApplication.instance.setStyleSheet(globalStyle)

valframe=newQFrame() {

this.setObjectName("mainWindow")

// 以下为窗体展现定制,应该在show之前调用

// show以后再调用,会令窗体crash,你需要再次show

this.setWindowFlags(WindowType.FramelessWindowHint)

this.setAttribute(WidgetAttribute.WA_TranslucentBackground, true)

// 由于设定了不使用windows窗体,所以,请手动结束多余的进程

}

frame.show()

QApplication.exec

}

}

截图效果如下:

5b0dce3b9d8a572db153e6937df6934d.png

怎么样,开始有点意思了吧?30行代码连样式,其实想做漂亮的界面,也不是那么难吧!

安装Qt Jambi

回到最初点,首先还是要把环境搭建起来。先去上面的地址下载Qt Jambi 4.5.2_01Binary for Windows 32-bit,随便解压吧。解开目录,里面有几个值得一看的东西:

4362a63c1067cf0e5c1fd12dd3e2e1f3.png

qtjambi.exe这个仅仅是运行一个Demo示例,看看吧,里面很多东西都会给你带来不错的启发。但不得不说,Qt原版的Demo,那叫一个炫啊,Java真受冷落。

designer.bat这个是打开设计器的,实际设计器在这个目录下的bin目录里面。

qtjambi-4.5.2_01.jar

qtjambi-win32-msvc2005-4.5.2_01.jar这两个jar包是你在实际开发中需要使用的,你需要将这两个库引入到你的项目中。并且从文件名我们可以发现,他是使用vc2005(vc80),如果你没安装vs2005补丁,快去装一个吧。

好了,准备功夫就这么点,我们可以开始进一步的工作了!

填充基础界面

好了,我们该开始往这样一个界面里面加东西了,首先,他要有个标题栏,中间是他的视图展示部分,当然了,我们还可以加一个底部的状态条。实际上,使用设计器和标准的QMainWidget,我们能做得很好。但一方面我是代码控,而另一方面,我觉得通过代码,能展示更多感性方面的东西(透过Qt Jambi的Eclipse的插件,一个界面设计完成,它会自动帮你转换为一个Java的类,所以,你无需过分担心后续实际开发的复杂度。)。而且说实在的,他的设计器和Vs比,就差很多很多了。

既然有那么多想法,我们可以考虑给它添加一个Layout,哦,忘记说了,Qt遵循较为严格的对象机制,所有界面构造元素都继承自QWidget,而Layout,则继承自QLayout,而QLayout和QWidget则都来自QObject,当然,细分还有很多,但Layout和QWidget毕竟是大头。

刚才展示的两个例子中,QFrame和QLabel都继承自QWidget,所以你可以对他们对Show,而不用考虑窗口,父容器,你对哪个QWidget的实例调用了show方法,它就会是一个独立窗体,逻辑十分清晰。

Layout有两种声明方式(当然,new实例只有一种方式),所谓声明是指和QWidget产生关联。new QHBoxLayout(anyQWidget),或者anyQWidget.setLayout(anyQLayout)。一个QWidget实例不能进行(关联)两次以上布局,当你第二次对它进行布局关联时,系统会给你一个警告,但不会令程序或者窗体Crash。

package qt.demo

import com.trolltech.qt.gui._

import com.trolltech.qt.core.Qt._

object CustomWindow {

//=======================// 全局样式 //=======================//

valglobalStyle="""

* { font-family: Microsoft Yahei; font-size: 12px; color: #333; }

#mainWindow { border: 32px solid #ccc; border-image: url(classpath:qt/demo/resource/window.png) 32 stretch; }

#title { font-size: 13px; font-weight: bold; color: #000; }

#body { border: 1px solid #ccc; }

"""

//=======================// 主窗体 //=======================//

//

// 我们将frame变量转移到这里

// lazy关键字,表示调用时才实现该变量值

lazy valframe=newQFrame() {

this.setObjectName("mainWindow")

// 以下为窗体展现定制,应该在show之前调用

// show以后再调用,会令窗体crash,你需要再次show

this.setWindowFlags(WindowType.FramelessWindowHint)

this.setAttribute(WidgetAttribute.WA_TranslucentBackground, true)

// 由于设定了不使用windows窗体,所以,请手动结束多余的进程

}

// 为了进一步分工明确,我又把layout也挪出来了

// 这里采用声明关联

// 一个layout会默认撑满整个容器

lazy valframeLayout=newQVBoxLayout(frame) {

this.setMargin(0)  // Layout四边的margin,当然你也可以用setContentsMargin来设定四边的具体值

this.setSpacing(0) // 设定元件之间的间距

}

//=======================// 标题栏 //=======================//

//

// 要构造界面,未必需要全部都是widget

// layout也可以插入到layout中

lazy valtitleLayout= {

val_layout=newQHBoxLayout // 这是一个内部变量,不在类变量中

// 让他垂直居中,左对齐

_layout.setSpacing(5)

_layout.setAlignment(new Alignment(AlignmentFlag.AlignLeft, AlignmentFlag.AlignVCenter))

_layout.addWidget(titleIcon)

_layout.addWidget(titleText)

_layout // 这里等同于这个block return _layout

}

// 标题的元素构成

lazy valtitleIcon=newQLabel {

this.setPixmap(getIcon.pixmap(18))

// 指定他的具体宽高

this.setFixedSize(20, 20)

// 让该容器按照该宽高占位

this.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)

}

lazy valtitleText=newQLabel("这是一个标题栏") {

this.setObjectName("title")

// 只指定具体高度

this.setFixedHeight(20)

// 他的水平方向会100%的撑开,垂直方向按指定的高度占位

this.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

}

//=======================// body部分 //=======================//

lazy valbody= {

valwidget=newQWidget

widget.setObjectName("body")

widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)

widget

}

//=======================// foot部分 //=======================//

lazy valfoot=newQLabel("状态栏") {

this.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

this.setAlignment(new Alignment(AlignmentFlag.AlignRight, AlignmentFlag.AlignBottom))

}

//=======================// 快捷函数 //=======================//

defapp=QApplication.instance

defgetIcon=frame.style.standardIcon(QStyle.StandardPixmap.SP_MessageBoxInformation)

def main(args: Array[String]):Unit= {

QApplication.initialize(args)

app.setStyleSheet(globalStyle)

frameLayout.addLayout(titleLayout)

frameLayout.addWidget(body)

frameLayout.addWidget(foot)

frame.resize(370, 270) // 这里要计算样式中border-width的宽度,实际上我们期望这个窗体的内容大小在300, 200

frame.show()

QApplication.exec

}

}

效果截图:

04fa3a3b128f358e30930dc1e0f84267.png

从30行激增到100行,可能需要点耐性去读。但整个窗体的初步效果已经看出来了,而且,很显然,类似这样的窗体,我们完全可以将他封装成一个单独的类。当然模拟一个完整的窗口,也许需要更多更多的代码,但通过良性的封装,能很好的解决代码冗余的问题。

Qt的信号槽和事件

信号槽,是Qt中一个有趣的设定,这个也体现了Qt在Ui方面的一种经验的积累。

首先给出最最简单的信号槽的代码样例。

class ClickEvent(widget: QWidget) {

def doClicked(checked: Int) {

// 若干操作

}

}

valbtn=newQPushButton()

btn.clicked.connect(anyInstance, "doClicked(int)")

好了,当你点击了btn以后,他会自动执行doClicked方法。也许你会说,切,这有什么了不起的呢?如果要细说,恐怕不是一时半会能说的清楚的,大家也许还焦急着进一步完善刚才做出来的窗口。OK,我长话短说。

在Qt所有类的上层,Event接口,是由QObject去定义的,而QWidget实际上是继承自QObject的。QObject::installEventFilter(QObject),实际上是注册事件的总入口。当然,在你每次实例一个QObject的时候,他已经默认的帮你为当前实例installEventFilter。这个方法实际上就是指派给具体哪个实例作为该对象的事件观察者。

从installEventFileter以后,事件首先经过evnetFilter进行分发,event应该是同级的事件分发。在这两个方法里,需要指定返回Boolean类型,实际上是对事件的拦截。

再其次,到各种种类繁多,因应具体类而产生的event入口,比如showEvent等等。在这个层面,事件经由上层的分发,已经不会再等待你返回结果(他是返回无类型的),虽然对于QEvent实例,有accept或者igroe,但实际上这个过程里,你已无法保证绝对控制事件的整体了,你只能控制经由上层分到你这一级以后的事情,可是上层做了什么,你不知道,也管不了。

在这个事件机制的基础之下,信号槽似乎是处于整个事件机制的***端,但信号槽机制又有其主要特点。他是单向的,不管理该实例的全局状态,他只关心他最关心(当然也是指派给他)的对象,这个(或若干个)对象往往是整个事件机制中最关键的一个状态,他提供给你一个既不用单独注册一个QObject以从头接管实例的全部事件,也不用利用override的方式重载事件声明,即可利用信号槽,去做其他关联操作的触发。而无论他有或者无,不会对实例本身的状态进行改变,所以他是一个松耦合、无歧义的接口。

而且,Qt提供给你自己扩展信号槽的机会,除了常规的Qt类可获得信号槽以外,在你自定义的类中,也可以通过继承自QSignalEmitter,来获得信号槽机制的使用。

好了废话一大堆,让我们继续干活,这次,我们添加一个关闭按钮,并且,让这个窗口可以被自由的拖动,而这也将是***将要完成的工作:

package qt.demo

import com.trolltech.qt.gui._

import com.trolltech.qt.core._

import com.trolltech.qt.core.Qt._

object CustomWindow {

//=======================// 全局样式 //=======================//

valglobalStyle="""

* { font-family: Microsoft Yahei; font-size: 12px; color: #333; }

#mainWindow { border: 32px solid #ccc; border-image: url(classpath:qt/demo/resource/window.png) 32 stretch; }

#title { font-size: 13px; font-weight: bold; color: #000; }

#body { border: 1px solid #ccc; }

#closeBtn { border: 0; background: none; font-weight: bold; color: red; }

"""

//=======================// 主窗体 //=======================//

// 其实我们可以将这个Frame作为一个单独类封装一下

class DemoFrame extends QFrame {

this.setObjectName("mainWindow")

// 以下为窗体展现定制,应该在show之前调用

// show以后再调用,会令窗体crash,你需要再次show

this.setWindowFlags(WindowType.FramelessWindowHint)

this.setAttribute(WidgetAttribute.WA_TranslucentBackground, true)

valframeLayout=newQVBoxLayout(this) {

this.setMargin(0)  // Layout四边的margin,当然你也可以用setContentsMargin来设定四边的具体值

this.setSpacing(0) // 设定元件之间的间距

}

private defframe=this

//=======================// 标题栏 //=======================//

valtitleLayout=newQHBoxLayout {

setSpacing(5)

setAlignment(new Alignment(AlignmentFlag.AlignLeft, AlignmentFlag.AlignVCenter))

}

// 标题的元素构成

valtitleIcon=newQLabel {

this.setPixmap(getIcon.pixmap(18))

// 指定他的具体宽高

this.setFixedSize(20, 20)

// 让该容器按照该宽高占位

this.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)

}

valtitleText=newQLabel("这是一个标题栏") {

this.setObjectName("title")

// 只指定具体高度

this.setFixedHeight(20)

// 他的水平方向会100%的撑开,垂直方向按指定的高度占位

this.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

// 我们从这里注册title拖动

// 其实***是title首先是个QWidget,那么我们可以对整个title进行拖动,请原谅我的懒惰

valdragPosition=newQPoint()

// 鼠标单击放下

override def mousePressEvent(event: QMouseEvent) {

if (event.button() == MouseButton.LeftButton) {

valtopLeft=frame.frameGeometry.topLeft

dragPosition.setX(event.globalPos.x - topLeft.x)

dragPosition.setY(event.globalPos.y - topLeft.y)

event.accept

}

}

override def mouseMoveEvent(event: QMouseEvent) {

if (event.buttons().isSet(MouseButton.LeftButton)) {

valtopLeft=frame.frameGeometry.topLeft

valp=newQPoint(event.globalPos().x() - dragPosition.x,

event.globalPos().y() - dragPosition.y)

frame.move(p)

event.accept

}

}

}

valcloseBtn=newQPushButton("X")

closeBtn.setObjectName("closeBtn")

closeBtn.setFixedSize(15, 15)

// 其实这里可以写成 this.clicked.connect(QApplication.instance, "quit()")

// close表示关闭frame窗口,quit则是整个程序退出

// qt内部会做调整,当你close窗口,又没有其他窗口在show或者准备show,他也会自动进行quit

closeBtn.clicked.connect(this, "close()")

override def showEvent(event: QShowEvent) {

valg=this.frameGeometry

val (w, h) = (g.width + 70, g.height + 70)

this.setFixedSize(w, h) // 根据样式重设大小

closeBtn.move(w - 45, 30)

}

//=======================// body部分 //=======================//

valbody= {

valwidget=newQWidget

widget.setObjectName("body")

widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)

widget

}

//=======================// foot部分 //=======================//

valfoot=newQLabel("状态栏") {

this.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

this.setAlignment(new Alignment(AlignmentFlag.AlignRight, AlignmentFlag.AlignBottom))

}

this.init()

def init() {

titleLayout.addWidget(titleIcon)

titleLayout.addWidget(titleText)

frameLayout.addLayout(titleLayout)

frameLayout.addWidget(body)

frameLayout.addWidget(foot)

closeBtn.setParent(this) // closeBtn***填充,因为他不在布局模式中。

}

defgetIcon=frame.style.standardIcon(QStyle.StandardPixmap.SP_MessageBoxInformation)

}

defapp=QApplication.instance

lazy valframe=newDemoFrame

def main(args: Array[String]):Unit= {

QApplication.initialize(args)

app.setStyleSheet(globalStyle)

frame.resize(300, 200) // 我们可以自由的设定这个部件的大小了,调整元件的大小被我们送去showEvent里面去了

frame.show()

QApplication.exec

}

}

好了,弄了这么半天,这个窗口也终于算是做成了,这是最终的效果,虽然和之前的版本没有什么差别,可是能拖动,效果却很不一样了。***来个留影合照哈!

f7f9fd15033c3ba0ad27bdf46ccec673.png

后记

Qt由于是基于C++的,所以除了UI界面方面,在很多细节都进行封装,比如有QString,QDevice,QByteArray等。而在HTTP、图形渲染、opengl、数据库驱动等方面类库,Qt也都一丝不苟的移植到了Qt Jambi上。事实上,Qt似乎包揽了作为客户端开放的方方面面,当然,其实Java自身也包含了JDBC、Swing等,权当是做一个比较吧。

【编辑推荐】

【责任编辑:立方 TEL:(010)68476606】

点赞 0

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值