StupidBeauty
Read times:917Posted at:Thu Jan 30 23:51:39 2014
- no title specified

Qt®4的Ruby教程翻译:第12章:这砣砖竟能浮于空中,Chapter 12: Hanging in the Air the Way Bricks Don't

Screenshot: Hanging in the Air the Way Bricks Don'tScreenshot: Hanging in the Air the Way Bricks Don't


文件:

概述

在这个示例里,我们扩展了LCDRange类,在其中加入了一个文字标签。我们还加入了一个射击目标。

一行行地研究

lcdrange.rb

def initialize(s, parent = nil)

super(parent)

init()

setText(s)

end

构造函数首先调用init(),然后设置标签上的文字。init()是一个用于做初始化的独立的函数,把它单独作为一个函数主要是因为这个教程的原始C++版本里面是使用函数重载来实现初始化的。

def init()

lcd = Qt::LCDNumber.new(2)

lcd.setSegmentStyle(Qt::LCDNumber::Filled)

@slider = Qt::Slider.new(Qt::Horizontal)

@slider.setRange(0, 99)

@slider.setValue(0)

@label = Qt::Label.new()

@label.setAlignment(Qt::AlignHCenter.to_i | Qt::AlignTop.to_i)

connect(@slider, SIGNAL('valueChanged(int)'),

lcd, SLOT('display(int)'))

connect(@slider, SIGNAL('valueChanged(int)'),

self, SIGNAL('valueChanged(int)'))

layout = Qt::VBoxLayout.new()

layout.addWidget(lcd)

layout.addWidget(@slider)

layout.addWidget(@label)

setLayout(layout)

setFocusProxy(@slider)

end

lcdslider的设置代码是与前一章相同的。接下来我们创建一个Qt::Label,并且让它的内容在水平方向居中显示,在竖直方向置顶显示。那些Qt::Object::connect()代码也是从前一章中直接山寨过来的。

def setText(s)

@label.setText(s)

end

这个函数设置文本标签的文字内容。

cannon.rb

CannonField现在有了两个新的信号:hit()missed()。另外,它还会记录一个射击目标。

signals 'hit()', 'missed()' #...

当子弹命中目标时,就会发射hit()信号。当子弹飞出了本部件的右边界或底部边界时(也就是说,已经确定它没有命中也不会再命中目标了),会发射missed()信号。

newTarget()

这行代码加入到了构造函数中。它为目标物体计算出一个“随机”位置。事实上,newTarget()函数会尝试着绘制出目标物体。因为我们此时还是在构造函数的代码中,所以CannonField部件此时还是不可见的。Qt能够确保当妳在一个隐藏部件上调用Qt::Widget::update()时不会产生破坏效果。

@@first_time = true

def newTarget()

if @@first_time

@@first_time = false

midnight = Qt::Time.new(0, 0, 0)

srand(midnight.secsTo(Qt::Time.currentTime()))

end

@target = Qt::Point.new(200 + rand(190), 10 + rand(255))

update()

end

这个函数创建一个其中心点位于随机位置的目标物体。

我们创建一个Qt::Time对象midnight,它表示的时间是00:00:00。然后,我们计算出自午夜到现在的秒数,将这个秒数作为随机数种子。参考Qt::DateQt::TimeQt::DateTime的文档,以了解更多信息。

最后,我们计算出目标的中心点。我们保持它位于(x = 200, y = 35, width = 190, height = 255)(也就是说,xy的取值范围分别是200到389和35到289)这个矩形中,所使用的是这样的坐标系统:y坐标轴的位置0位于本部件的底部边缘,并且y的值向上增加,x是正常的,0位于左边缘,x的值向右增加。

我们已经做过试验,子弹能够达到这整个范围。

def moveShot()

region = Qt::Region.new(shotRect())

@timerCount += 1

shotR = shotRect()

这部分的定时器事件代码跟前一章中相同。

if shotR.intersects(targetRect())

@autoShootTimer.stop()

emit hit()

这段if语句会检查子弹的矩形区域是否与目标的矩形区域相交。如果是相交的,那么子弹就已经命中目标(精彩!)。我们停掉子弹定时器,并且发射hit()信号,向外界告知某个目标已经被摧毁,然后返回。注意,我们完全可以做到当场再创建一个新的目标,但是,由于CannonField是一个组件,所以,我们将决定权留给此组件的用户。

elsif shotR.x() > width() || shotR.y() > height()

@autoShootTimer.stop()

emit missed()

这段代码与前一章类似,不同点就在于它会发射missed()信号,以向外界告知打炮失败的消息。

else

region = region.unite(Qt::Region.new(shotR))

end

update(region)

end

然后,这个函数剩下的部分就跟之前章节一样。

CannonField::paintEvent()与之前章节一样,只是加入了这一行:

paintTarget(painter)

这行代码会确保,在必要的时候也会绘制射击目标。

def paintTarget(painter)

painter.setBrush(Qt::Brush.new(Qt::red))

painter.setPen(Qt::Pen.new(Qt::Color.new(Qt::black)))

painter.drawRect(targetRect())

end

这个函数会绘制射击目标;即为一个边缘为黑色、由红色填充的矩形。

def targetRect()

result = Qt::Rect.new(0, 0, 20, 10)

result.moveCenter(Qt::Point.new(@target.x(), height() - 1 - @target.y()))

return result

end

这个私有函数会返回射击目标的矩形区域。还记得吗,在newTarget()中,target这个坐标点的计算过程中,会将y轴的0坐标放置在本部件的底部。在我们调用Qt::Rect::moveCenter()之前,先按照部件本身的坐标系来计算出这个点的坐标。

我们选择采用这种坐标系映射手段的原因就是,为了让射击目标与部件底部之间的距离保持固定。别忘了,本部件可能在任何时候被用户或程序本身改变大小。

t12.rb

MyWidget 类中没有新的成员,但是我们稍微改变了一下构造函数,以设置新的LCDRange 对象的文本标签文字。

angle = LCDRange.new(tr('ANGLE'))

我们将角度文字标签的内容设置成"ANGLE"。

force = LCDRange.new(tr('FORCE'))

我们将力道文字标签的内容设置成"FORCE"。

运行程序

LCDRange部件看起来会有点奇怪:当我们改变MyWidget的大小时,Qt::VBoxLayout中的内置布局管理器会给文本标签分配太多的空间,而其它的部件却得不到足够的空间;导致两个LCDRange部件之间的空间会改变大小。我们将在下一章中解决这个问题

练习

创建一个作弊按钮,当这个按钮被按时,让CannonField显示出射击策略,并且显示5秒。

如果妳做了前一章中的"圆形子弹"练习的话,那么,这次试着将shotRect()改变成shotRegion(),返回一个Qt::Region,以实现更精确的碰撞检测。

创建一个移动的目标。

确保射击目标每次被创建时都整个处于屏幕上的可见区域。

确保这个部件不可被改变大小,这样射击目标就不会突然变得不可见了。[提示:可试试使用Qt::Widget::setMinimumSize()来做到这一点。]

这个有点难;让这个程序支持多个子弹同时处于空中飞翔。[提示:创建一个专门的Shot类。]

[下一篇: 13章 ]

自适应阈值化

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands