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

From KDE TechBase
(Created page with '{{Template:I18n/Language Navigation Bar_(zh_TW)|Development/Tutorials/Qt4 Ruby Tutorial/Chapter 12}} {{TutorialBrowser_(zh_TW)| series=[[Development/Tutorials/Qt4_Ruby_Tutorial...')
 
No edit summary
Line 19: Line 19:


===概覽===
===概覽===
In this example, we extend our '''<tt>LCDRange</tt>''' class to include a text label. We also provide something to shoot at. 
在這個範例中,我們擴充我們的 '''<tt>LCDRange</tt>''' 類別來包含一個文字標籤。我們還提供一些用來射擊的目標。


===一行一行的瀏覽===
===一行一行的瀏覽===
Line 32: Line 32:
</code>
</code>


This constructor first calls '''<tt>init()</tt>''' and then sets the label text. '''<tt>init()</tt>''' is a separate function performing initialization mosty because of function overloading matters in the original C++ version.
這個建構子先呼叫 '''<tt>init()</tt>''',然後設定標籤文字。'''<tt>init()</tt>''' 是一個執行大部分初始化的獨立函式,因為在原來的C++版本中是函式重載。


<code ruby>
<code ruby>
Line 61: Line 61:
</code>
</code>


The setup of '''<tt>lcd</tt>''' and '''<tt>slider</tt>''' is the same as in the previous chapter. Next we create a [http://doc.qt.nokia.com/latest/qlabel.html Qt::Label] and tell it to align the contents centered horizontally and to the top vertically. The [http://doc.qt.nokia.com/latest/qobject.html#connect Qt::Object::connect()] calls have also been taken from the previous chapter.
'''<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>
<code ruby>
Line 69: Line 69:
</code>
</code>


This function sets the label text.
這個函式設定標籤文字。


'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t12/cannon.rb cannon.rb]'''
'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t12/cannon.rb cannon.rb]'''


The '''<tt>CannonField</tt>''' now has two new signals: '''<tt>hit()</tt>''' and '''<tt>missed()</tt>'''. In addition, it contains a target.
'''<tt>CannonField</tt>''' 現在有兩個新的訊號:'''<tt>hit()</tt>''' '''<tt>missed()</tt>'''。此外,它還多了一個目標。


<code ruby>
<code ruby>
  signals 'hit()', 'missed()' #...
signals 'hit()', 'missed()' #...
</code>
</code>


The '''<tt>hit()</tt>''' signal is emitted when a shot hits the target. The '''<tt>missed()</tt>''' signal is emitted when the shot moves beyond the right or bottom edge of the widget (i.e., it is certain that it has not and will not hit the target).
一旦砲彈擊中了目標,'''<tt>hit()</tt>''' 訊號就會被發出。而當砲彈越過了 widget 右側或底部的邊緣,'''<tt>missed()</tt>''' 訊號就會被發出(換言之,可以肯定的是它不會命中目標)。


<code ruby>
<code ruby>
    newTarget()
newTarget()
</code>
</code>


This line has been added to the constructor. It creates a "random" position for the target. In fact, the '''<tt>newTarget()</tt>''' function will try to paint the target. Because we are in a constructor, the '''<tt>CannonField</tt>''' widget is invisible. Qt guarantees that no harm is done when calling [http://doc.qt.nokia.com/latest/qwidget.html#update Qt::Widget::update()] on a hidden widget.
這行已經加入到建構子中。它為目標建立了一個「隨機」的位置。事實上,'''<tt>newTarget()</tt>''' 函式將嘗試畫出目標。因為我們是在建構子中,'''<tt>CannonField</tt>''' widget 是不可見的。Qt 保證在一個隱藏的 widget 中呼叫[http://doc.qt.nokia.com/latest/qwidget.html#update Qt::Widget::update()] 是無害。


<code ruby>
<code ruby>
Line 102: Line 102:
</code>
</code>


This function creates a target center point at a new random position.
這個函式會在新的隨機位置建立一個目標中心點。


We create the [http://doc.qt.nokia.com/latest/qtime.html Qt::Time] object '''<tt>midnight</tt>''', which represents the time 00:00:00. Next we fetch the number of seconds from midnight until now and use it as a random seed. See the documentation for [http://doc.qt.nokia.com/latest/qdate.html Qt::Date], [http://doc.qt.nokia.com/latest/qtime.html Qt::Time], and [http://doc.qt.nokia.com/latest/qdatetime.html Qt::DateTime] for more information.
We create the [http://doc.qt.nokia.com/latest/qtime.html Qt::Time] object '''<tt>midnight</tt>''', which represents the time 00:00:00. Next we fetch the number of seconds from midnight until now and use it as a random seed. See the documentation for [http://doc.qt.nokia.com/latest/qdate.html Qt::Date], [http://doc.qt.nokia.com/latest/qtime.html Qt::Time], and [http://doc.qt.nokia.com/latest/qdatetime.html Qt::DateTime] for more information.

Revision as of 16:41, 15 January 2010

Template:I18n/Language Navigation Bar (zh TW)

Template:TutorialBrowser (zh TW)

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

lcd 的結構和前面章節的 slider 是一樣的。接下來,我們建立一個 Qt::Label,並告訴它以水平置中和與垂直向上的方式對齊內容。Qt::Object::connect() 呼叫也取自前面章節。

def setText(s)

 @label.setText(s)

end

這個函式設定標籤文字。

cannon.rb

CannonField 現在有兩個新的訊號:hit()missed()。此外,它還多了一個目標。

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

一旦砲彈擊中了目標,hit() 訊號就會被發出。而當砲彈越過了 widget 右側或底部的邊緣,missed() 訊號就會被發出(換言之,可以肯定的是它不會命中目標)。

newTarget()

這行已經加入到建構子中。它為目標建立了一個「隨機」的位置。事實上,newTarget() 函式將嘗試畫出目標。因為我們是在建構子中,CannonField widget 是不可見的。Qt 保證在一個隱藏的 widget 中呼叫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

這個函式會在新的隨機位置建立一個目標中心點。

We create the Qt::Time object midnight, which represents the time 00:00:00. Next we fetch the number of seconds from midnight until now and use it as a random seed. See the documentation for Qt::Date, Qt::Time, and Qt::DateTime for more information.

Finally we calculate the target's center point. We keep it within the rectangle (x = 200, y = 35, width = 190, height = 255), i.e., the possible x and y values are 200 to 389 and 35 to 289, respectively) in a coordinate system where we put y position 0 at the bottom edge of the widget and let y values increase upwards x is as normal, with 0 at the left edge and with x values increasing to the right.

By experimentation we have found this to always be in reach of the shot.

def moveShot()

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

This part of the timer event has not changed from the previous chapter.

if shotR.intersects(targetRect())

 @autoShootTimer.stop()
 emit hit()

This if statement checks whether the shot rectangle intersects the target rectangle. If it does, the shot has hit the target (ouch!). We stop the shoot timer and emit the hit() signal to tell the outside world that a target was destroyed, and return. Note that we could have created a new target on the spot, but because the CannonField is a component we leave such decisions to the user of the component.

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

 @autoShootTimer.stop()
 emit missed()

This is the same as in the previous chapter, except that it now emits the missed() signal to tell the outside world about the failure.

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

end

And the rest of the function is as before.

CannonField::paintEvent() is as before, except that this has been added:

   paintTarget(painter)

This line makes sure that the target is also painted when necessary.

def paintTarget(painter)

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

end

This function paints the target; a rectangle filled with red and with a black outline.

def targetRect()

 result = Qt::Rect.new(0, 0, 20, 10)
 result.moveCenter(Qt::Point.new(@target.x(), height() - 1 - @target.y()))
 return result

end

This private function returns the enclosing rectangle of the target. Remember from newTarget() that the target point uses y coordinate 0 at the bottom of the widget. We calculate the point in widget coordinates before we call Qt::Rect::moveCenter().

The reason we have chosen this coordinate mapping is to fix the distance between the target and the bottom of the widget. Remember that the widget can be resized by the user or the program at any time.

t12.rb

There are no new members in the MyWidget class, but we have slightly changed the constructor to set the new LCDRange text labels.

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

We set the angle text label to "ANGLE".

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

We set the force text label to "FORCE".

執行應用程式

The LCDRange widgets look a bit strange: When resizing MyWidget, the built-in layout management in Qt::VBoxLayout gives the labels too much space and the rest not enough; making the space between the two LCDRange widgets change size. We'll fix that in the next chapter

練習

Make a cheat button that, when pressed, makes the CannonField display the shot trajectory for five seconds.

If you did the "round shot" exercise from the previous chapter, try changing the shotRect() to a shotRegion() that returns a Qt::Region so you can have really accurate collision detection.

Make a moving target.

Make sure that the target is always created entirely on-screen.

Make sure that the widget cannot be resized so that the target isn't visible. [Hint: Qt::Widget::setMinimumSize() is your friend.]

Not easy; make it possible to have several shots in the air at the same time. [Hint: Make a Shot class.]