Archive:Development/Tutorials/Qt4 Ruby Tutorial/Chapter 12 (zh CN): Difference between revisions

From KDE TechBase
(Created page with '{{Template:I18n/Language Navigation Bar_(zh_CN)|Development/Tutorials/Qt4 Ruby Tutorial/Chapter 12}} {{TutorialBrowser_(zh_CN)| series=[[Development/Tutorials/Qt4_Ruby_Tutorial...')
 
m (Text replace - "<code ruby>" to "<syntaxhighlight lang="ruby">")
Line 24: Line 24:
'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t12/lcdrange.rb lcdrange.rb]'''
'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t12/lcdrange.rb lcdrange.rb]'''


<code ruby>
<syntaxhighlight lang="ruby">
def initialize(s, parent = nil)
def initialize(s, parent = nil)
   super(parent)
   super(parent)
Line 34: Line 34:
这个建构子先呼叫 '''<tt>init()</tt>''',然后设定卷标文字。'''<tt>init()</tt>''' 是一个执行大部分初始化的独立函式,因为在原来的C++版本中是函式重载。
这个建构子先呼叫 '''<tt>init()</tt>''',然后设定卷标文字。'''<tt>init()</tt>''' 是一个执行大部分初始化的独立函式,因为在原来的C++版本中是函式重载。


<code ruby>
<syntaxhighlight lang="ruby">
def init()
def init()
   lcd = Qt::LCDNumber.new(2)
   lcd = Qt::LCDNumber.new(2)
Line 63: Line 63:
'''<tt>lcd</tt>''' 的结构和前面章节的 '''<tt>slider</tt>''' 是一样的。接下来,我们建立一个 [http://doc.qt.nokia.com/latest/qlabel.html Qt::Label],并告诉它以水平置中和与垂直向上的方式对齐内容。[http://doc.qt.nokia.com/latest/qobject.html#connect Qt::Object::connect()] 呼叫也取自前面章节。
'''<tt>lcd</tt>''' 的结构和前面章节的 '''<tt>slider</tt>''' 是一样的。接下来,我们建立一个 [http://doc.qt.nokia.com/latest/qlabel.html Qt::Label],并告诉它以水平置中和与垂直向上的方式对齐内容。[http://doc.qt.nokia.com/latest/qobject.html#connect Qt::Object::connect()] 呼叫也取自前面章节。


<code ruby>
<syntaxhighlight lang="ruby">
def setText(s)
def setText(s)
   @label.setText(s)
   @label.setText(s)
Line 75: Line 75:
'''<tt>CannonField</tt>''' 现在有两个新的讯号:'''<tt>hit()</tt>''' 和 '''<tt>missed()</tt>'''。此外,它还多了一个目标。
'''<tt>CannonField</tt>''' 现在有两个新的讯号:'''<tt>hit()</tt>''' 和 '''<tt>missed()</tt>'''。此外,它还多了一个目标。


<code ruby>
<syntaxhighlight lang="ruby">
signals 'hit()', 'missed()' #...
signals 'hit()', 'missed()' #...
</code>
</code>
Line 81: Line 81:
一旦炮弹击中了目标,'''<tt>hit()</tt>''' 讯号就会被发出。而当炮弹越过了 widget 右侧或底部的边缘,'''<tt>missed()</tt>''' 讯号就会被发出(换言之,可以肯定的是它不会命中目标)。
一旦炮弹击中了目标,'''<tt>hit()</tt>''' 讯号就会被发出。而当炮弹越过了 widget 右侧或底部的边缘,'''<tt>missed()</tt>''' 讯号就会被发出(换言之,可以肯定的是它不会命中目标)。


<code ruby>
<syntaxhighlight lang="ruby">
newTarget()
newTarget()
</code>
</code>
Line 87: Line 87:
这行已经加入到建构子中。它为目标建立了一个「随机」的位置。事实上,'''<tt>newTarget()</tt>''' 函式将尝试画出目标。因为我们是在建构子中,'''<tt>CannonField</tt>''' widget 是不可见的。Qt 保证在一个隐藏的 widget 中呼叫[http://doc.qt.nokia.com/latest/qwidget.html#update Qt::Widget::update()] 是无害。
这行已经加入到建构子中。它为目标建立了一个「随机」的位置。事实上,'''<tt>newTarget()</tt>''' 函式将尝试画出目标。因为我们是在建构子中,'''<tt>CannonField</tt>''' widget 是不可见的。Qt 保证在一个隐藏的 widget 中呼叫[http://doc.qt.nokia.com/latest/qwidget.html#update Qt::Widget::update()] 是无害。


<code ruby>
<syntaxhighlight lang="ruby">
@@first_time = true
@@first_time = true


Line 110: Line 110:
通过实验我们发现这样炮弹都可以击中。
通过实验我们发现这样炮弹都可以击中。


<code ruby>
<syntaxhighlight lang="ruby">
def moveShot()
def moveShot()
   region = Qt::Region.new(shotRect())
   region = Qt::Region.new(shotRect())
Line 120: Line 120:
来自前面章节的这部分定时器事件并没有改变。
来自前面章节的这部分定时器事件并没有改变。


<code ruby>
<syntaxhighlight lang="ruby">
if shotR.intersects(targetRect())  
if shotR.intersects(targetRect())  
   @autoShootTimer.stop()
   @autoShootTimer.stop()
Line 128: Line 128:
这个 '''<tt>if</tt>''' 叙述检查炮弹矩形是否与目标矩形相交。如果是这样,炮弹会击中了目标(哎哟!)。我们停止射击定时器,并发出 '''<tt>hit()</tt>''' 讯号来告诉外界目标被摧毁,并且返回。请注意,我们可以在这个点上建立一个新的目标,但是因为 '''<tt>CannonField</tt>''' 是一个组件,我们把这样的决定留给组件的用户。
这个 '''<tt>if</tt>''' 叙述检查炮弹矩形是否与目标矩形相交。如果是这样,炮弹会击中了目标(哎哟!)。我们停止射击定时器,并发出 '''<tt>hit()</tt>''' 讯号来告诉外界目标被摧毁,并且返回。请注意,我们可以在这个点上建立一个新的目标,但是因为 '''<tt>CannonField</tt>''' 是一个组件,我们把这样的决定留给组件的用户。


<code ruby>
<syntaxhighlight lang="ruby">
elsif shotR.x() > width() || shotR.y() > height()
elsif shotR.x() > width() || shotR.y() > height()
   @autoShootTimer.stop()
   @autoShootTimer.stop()
Line 136: Line 136:
这与前面章节是一样的,但它现在发出 '''<tt>missed()</tt>''' 讯号来告诉外界射击失败。
这与前面章节是一样的,但它现在发出 '''<tt>missed()</tt>''' 讯号来告诉外界射击失败。


<code ruby>
<syntaxhighlight lang="ruby">
   else
   else
     region = region.unite(Qt::Region.new(shotR))
     region = region.unite(Qt::Region.new(shotR))
Line 149: Line 149:
'''<tt>CannonField::paintEvent()</tt>''' 和之前相同,除了加入这个:
'''<tt>CannonField::paintEvent()</tt>''' 和之前相同,除了加入这个:


<code ruby>
<syntaxhighlight lang="ruby">
     paintTarget(painter)
     paintTarget(painter)
</code>
</code>
Line 155: Line 155:
这行可以确保在必要时目标也会画出。
这行可以确保在必要时目标也会画出。


<code ruby>
<syntaxhighlight lang="ruby">
def paintTarget(painter)
def paintTarget(painter)
   painter.setBrush(Qt::Brush.new(Qt::red))
   painter.setBrush(Qt::Brush.new(Qt::red))
Line 165: Line 165:
这个函式绘制目标;一个填充了红色和黑色外框的长方形。
这个函式绘制目标;一个填充了红色和黑色外框的长方形。


<code ruby>
<syntaxhighlight lang="ruby">
def targetRect()
def targetRect()
   result = Qt::Rect.new(0, 0, 20, 10)
   result = Qt::Rect.new(0, 0, 20, 10)
Line 181: Line 181:
'''<tt>MyWidget</tt>''' 类别中没有新的成员。但我们现在稍微修改建构子来设定新的 '''<tt>LCDRange</tt>''' 文字卷标。
'''<tt>MyWidget</tt>''' 类别中没有新的成员。但我们现在稍微修改建构子来设定新的 '''<tt>LCDRange</tt>''' 文字卷标。


<code ruby>
<syntaxhighlight lang="ruby">
     angle = LCDRange.new(tr('ANGLE'))
     angle = LCDRange.new(tr('ANGLE'))
</code>
</code>
Line 187: Line 187:
我们设定了角度文字卷标为「ANGLE」。
我们设定了角度文字卷标为「ANGLE」。


<code ruby>
<syntaxhighlight lang="ruby">
     force  = LCDRange.new(tr('FORCE'))
     force  = LCDRange.new(tr('FORCE'))
</code>
</code>

Revision as of 20:43, 29 June 2011

Template:I18n/Language Navigation Bar (zh CN)

Template:TutorialBrowser (zh CN)

Hanging in the Air the Way Bricks Don't

档案:

概览

在这个范例中,我们扩充我们的 LCDRange 类别来包含一个文字卷标。我们还提供一些用来射击的目标。

一行一行的浏览

lcdrange.rb

<syntaxhighlight lang="ruby"> def initialize(s, parent = nil)

 super(parent)
 init()
 setText(s)

end

这个建构子先呼叫 init(),然后设定卷标文字。init() 是一个执行大部分初始化的独立函式,因为在原来的C++版本中是函式重载。

<syntaxhighlight lang="ruby"> 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

lcd 的结构和前面章节的 slider 是一样的。接下来,我们建立一个 Qt::Label,并告诉它以水平置中和与垂直向上的方式对齐内容。Qt::Object::connect() 呼叫也取自前面章节。

<syntaxhighlight lang="ruby"> def setText(s)

 @label.setText(s)

end

这个函式设定卷标文字。

cannon.rb

CannonField 现在有两个新的讯号:hit()missed()。此外,它还多了一个目标。

<syntaxhighlight lang="ruby"> signals 'hit()', 'missed()' #...

一旦炮弹击中了目标,hit() 讯号就会被发出。而当炮弹越过了 widget 右侧或底部的边缘,missed() 讯号就会被发出(换言之,可以肯定的是它不会命中目标)。

<syntaxhighlight lang="ruby"> newTarget()

这行已经加入到建构子中。它为目标建立了一个「随机」的位置。事实上,newTarget() 函式将尝试画出目标。因为我们是在建构子中,CannonField widget 是不可见的。Qt 保证在一个隐藏的 widget 中呼叫Qt::Widget::update() 是无害。

<syntaxhighlight lang="ruby"> @@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。接下来,我们取出从午夜到目前为止经过的秒数,并使用它作为随机数子(random seed)。请参阅文件 Qt::DateQt::Time 和Qt::DateTime 以取得更多信息。

最后,我们计算出目标的中心点。我们保持它在 widget 的底边为 y 值0,让 y 值向上增加,x 是正常的左侧边缘为0,以及右侧增加x值的坐标系统中的矩形(x=200,y=35,宽=190,高=255。换句话说,可能的 x 和 y 值分别是200至389和35至289)。

通过实验我们发现这样炮弹都可以击中。

<syntaxhighlight lang="ruby"> def moveShot()

 region = Qt::Region.new(shotRect())
 @timerCount += 1
 shotR = shotRect()

来自前面章节的这部分定时器事件并没有改变。

<syntaxhighlight lang="ruby"> if shotR.intersects(targetRect())

 @autoShootTimer.stop()
 emit hit()

这个 if 叙述检查炮弹矩形是否与目标矩形相交。如果是这样,炮弹会击中了目标(哎哟!)。我们停止射击定时器,并发出 hit() 讯号来告诉外界目标被摧毁,并且返回。请注意,我们可以在这个点上建立一个新的目标,但是因为 CannonField 是一个组件,我们把这样的决定留给组件的用户。

<syntaxhighlight lang="ruby"> elsif shotR.x() > width() || shotR.y() > height()

 @autoShootTimer.stop()
 emit missed()

这与前面章节是一样的,但它现在发出 missed() 讯号来告诉外界射击失败。

<syntaxhighlight lang="ruby">

 else
   region = region.unite(Qt::Region.new(shotR))
 end
   
 update(region)

end

而函式的剩下部分与之前一样。

CannonField::paintEvent() 和之前相同,除了加入这个:

<syntaxhighlight lang="ruby">

   paintTarget(painter)

这行可以确保在必要时目标也会画出。

<syntaxhighlight lang="ruby"> def paintTarget(painter)

 painter.setBrush(Qt::Brush.new(Qt::red))
 painter.setPen(Qt::Pen.new(Qt::Color.new(Qt::black)))
 painter.drawRect(targetRect())

end

这个函式绘制目标;一个填充了红色和黑色外框的长方形。

<syntaxhighlight lang="ruby"> 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 点使用 widget 的底部为 y 坐标0。,我们在呼叫 Qt::Rect::moveCenter() 之前,在 widget 坐标中计算这个点。

我们之所以选择这个坐标映像(coordinate mapping)是为了要固定目标和 widget 底部之间的距离。请记住,用户或程序可以在任何时候改变 widget 大小。

t12.rb

MyWidget 类别中没有新的成员。但我们现在稍微修改建构子来设定新的 LCDRange 文字卷标。

<syntaxhighlight lang="ruby">

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

我们设定了角度文字卷标为「ANGLE」。

<syntaxhighlight lang="ruby">

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

我们设定了力量文字卷标为「FORCE」。

执行应用程序

LCDRange widgets 看起来有点怪:当 MyWidget 大小改变时,Qt::VBoxLayout 内建的布局管理给标签太多的空间了,其它的就不够用;使得两个 LCDRange widget 之间的空间改变大小。我们将在下一章修正。

练习

做一个作弊按钮。当按下时,使 CannonField 显示5秒的射击轨迹。

如果你做了前一章的「圆形炮弹」练习,请尝试把 shotRect() 更改为返回Qt::RegionshotRegion(),这样你就可以有相当精确的碰撞检测。

做一个移动的目标。

请确保目标建立时,总是完整的在屏幕上。

请确保这个 widget 不能重设大小,以免目标变成不可见的。[提示:Qt::Widget::setMinimumSize() 是你的好朋友。]

这不太容易。同一时间有多个炮弹在空中。[提示:建立 Shot 类别。]