Development/Tutorials/Qt4 Ruby Tutorial/Chapter 08/ru: Difference between revisions

From KDE TechBase
(Created page with "В этом примере мы вводим виджет, который сам занимается рисованием. Также добавляется управление...")
 
(Updating to match new version of source page)
 
(16 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages />
<languages />


{{Template:I18n/Language Navigation Bar|Development/Tutorials/Qt4 Ruby Tutorial/Chapter 08}}


{{TutorialBrowser|
{{TutorialBrowser/ru|
series=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial|Qt4 Ruby Tutorial]]|
series=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial|Введение в программирование на Qt<sup>®</sup>4 на языке Ruby]]|
name=Preparing for Battle|
name=Готовься к битве|
pre=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial/Chapter_07|Tutorial 7 - One Thing Leads to Another]]|
pre=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial/Chapter_07|Пример 7: Одно приводит к другому]]|
next=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial/Chapter_09|Tutorial 9 - With Cannon You Can]]
next=[[Special:myLanguage/Development/Tutorials/Qt4_Ruby_Tutorial/Chapter_09|Пример 9: Пушка детям не игрушка]]
}}
}}
== Preparing for Battle ==
== Готовься к битве ==
[[Image:Qt4_Ruby_Tutorial_Screenshot_8.png|center]]
[[Image:Qt4_Ruby_Tutorial_Screenshot_8.png|center]]


Files:
Файлы:
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/lcdrange.rb lcdrange.rb]
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/lcdrange.rb lcdrange.rb]
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/t8.rb t8.rb]
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/t8.rb t8.rb]
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/cannon.rb cannon.rb]
* [http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/cannon.rb cannon.rb]


=== Overview ===
=== Обзор ===


В этом примере мы вводим виджет, который сам занимается рисованием. Также добавляется управление с клавиатуры (это две строки кода).  
В этом примере мы вводим виджет, который сам занимается рисованием. Также добавляется управление с клавиатуры (это две строки кода).  


=== Line by Line Walkthrough ===
=== Построчный обзор программы ===
'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/lcdrange.rb lcdrange.rb]'''
'''[http://www.darshancomputing.com/qt4-qtruby-tutorial/tutorial/t8/lcdrange.rb lcdrange.rb]'''


This file is very similar to the lcdrange.rb in Chapter 7. We have added one slot: '''<tt>setRange()</tt>'''.
<span class="mw-translate-fuzzy">
Этот файл почти не отличается от lcdrange.rb из предыдущей главы. Мы добавили один слот: '''<tt>setRange()</tt>'''.
</span>


We now add the possibility of setting the range of the '''<tt>LCDRange</tt>'''. Until now, it has been fixed at 0 to 99.
Таким образом, мы добавляем возможность изменения диапазона возможных значений '''<tt>LCDRange</tt>'''. До этого диапазон был фиксирован: от 0 до 99.


<syntaxhighlight lang="ruby">
<syntaxhighlight lang="ruby">
Line 41: Line 42:
</syntaxhighlight>
</syntaxhighlight>


The '''<tt>setRange()</tt>''' slot sets the range of the slider in the '''<tt>LCDRange</tt>'''. Because we have set up the [http://doc.qt.nokia.com/latest/qlcdnumber.html QLCDNumber] to always display two digits, we want to limit the possible range of '''<tt>minVal</tt>''' and '''<tt>maxVal</tt>''' to avoid overflow of the [http://doc.qt.nokia.com/latest/qlcdnumber.html QLCDNumber]. (We could have allowed values down to -9 but chose not to.) If the arguments are illegal, we use Qt's [http://doc.qt.nokia.com/latest/qtglobal.html#qWarning QtGlobal::qWarning()] function to issue a warning to the user and return immediately. [http://doc.qt.nokia.com/latest/qtglobal.html#qWarning QtGlobal::qWarning()] is a '''<tt>printf</tt>'''-like function that by default sends its output to '''<tt>$stderr</tt>'''. If you want, you can install your own handler function using [http://doc.qt.nokia.com/latest/qtglobal.html#qInstallMsgHandler QtGlobal::qInstallMsgHandler()].
Слот '''<tt>setRange()</tt>''' устанавливает диапазон возможных значений ползунка '''<tt>LCDRange</tt>'''. Поскольку мы настроили [http://doc.qt.nokia.com/latest/qlcdnumber.html QLCDNumber] на показ двух цифр, нужно ограничить диапазон значений '''<tt>minVal</tt>''' и '''<tt>maxVal</tt>''', чтобы не происходило переполнения [http://doc.qt.nokia.com/latest/qlcdnumber.html QLCDNumber]. (Можно было разрешить отрицательные значения до -9, но мы решили этого не делать.) Если аргументы слота недопустимы, мы используем функцию Qt [http://doc.qt.nokia.com/latest/qtglobal.html#qWarning QtGlobal::qWarning()] для того, чтобы сообщить пользователю об ошибке. [http://doc.qt.nokia.com/latest/qtglobal.html#qWarning QtGlobal::qWarning()] работает аналогично '''<tt>printf</tt>''' и по умолчанию выводит сообщение в '''<tt>$stderr</tt>'''. Можно установить свой обработчик сообщений с помощью [http://doc.qt.nokia.com/latest/qtglobal.html#qInstallMsgHandler QtGlobal::qInstallMsgHandler()].


<syntaxhighlight lang="ruby">
<syntaxhighlight lang="ruby">
Line 179: Line 180:
Our last action is to set '''<tt>angle</tt>''' to have keyboard focus so that keyboard input will go to the '''<tt>LCDRange</tt>''' widget by default.
Our last action is to set '''<tt>angle</tt>''' to have keyboard focus so that keyboard input will go to the '''<tt>LCDRange</tt>''' widget by default.


=== Running the Application ===
=== Запуск приложения ===


When the slider is operated, the '''<tt>CannonField</tt>''' displays the new angle value. Upon resizing, '''<tt>CannonField</tt>''' is given as much space as possible.
When the slider is operated, the '''<tt>CannonField</tt>''' displays the new angle value. Upon resizing, '''<tt>CannonField</tt>''' is given as much space as possible.


=== Exercises ===
=== Упражнения ===


Try to resize the window. What happens if you make it really narrow or really squat?
Try to resize the window. What happens if you make it really narrow or really squat?
Line 193: Line 194:
Center the text in the '''<tt>CannonField</tt>'''.
Center the text in the '''<tt>CannonField</tt>'''.


[[Category:Ruby]]
[[Category:Ruby/ru]]

Latest revision as of 14:20, 18 July 2012

Other languages:


Готовься к битве
Серия примеров   Введение в программирование на Qt®4 на языке Ruby
Необходимо знать   Пример 7: Одно приводит к другому
Следующий пример   Пример 9: Пушка детям не игрушка
Литература   нет

Готовься к битве

Файлы:

Обзор

В этом примере мы вводим виджет, который сам занимается рисованием. Также добавляется управление с клавиатуры (это две строки кода).

Построчный обзор программы

lcdrange.rb

Этот файл почти не отличается от lcdrange.rb из предыдущей главы. Мы добавили один слот: setRange().

Таким образом, мы добавляем возможность изменения диапазона возможных значений LCDRange. До этого диапазон был фиксирован: от 0 до 99.

def setRange(minVal, maxVal)
  if minVal < 0 || maxVal > 99 || minVal > maxVal
    qWarning("LCDRange::setRange(#{minVal}, #{maxVal})\n" +
               "\tRange must be 0..99\n" +
               "\tand minVal must not be greater than maxVal")
    return
  end

  @slider.setRange(minVal, maxVal)
end

Слот setRange() устанавливает диапазон возможных значений ползунка LCDRange. Поскольку мы настроили QLCDNumber на показ двух цифр, нужно ограничить диапазон значений minVal и maxVal, чтобы не происходило переполнения QLCDNumber. (Можно было разрешить отрицательные значения до -9, но мы решили этого не делать.) Если аргументы слота недопустимы, мы используем функцию Qt QtGlobal::qWarning() для того, чтобы сообщить пользователю об ошибке. QtGlobal::qWarning() работает аналогично printf и по умолчанию выводит сообщение в $stderr. Можно установить свой обработчик сообщений с помощью QtGlobal::qInstallMsgHandler().

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

This makes our lcd numbers look way better. I'm not certain, but I believe what makes it possible to do this is setting a palette (see next section). What I do know is that this line has no effect when I tried it in previous chapters, but works here.

cannon.rb

@currentAngle = 45
setPalette(Qt::Palette.new(Qt::Color.new(250, 250, 200)))
setAutoFillBackground(true)

The constructor initializes the angle value to 45 degrees and sets a custom palette for this widget.

This palette uses the indicated color as background and picks other colors suitably. (For this widget only the background and text colors will actually be used.) We then call setAutoFillBackground(true) to tell Qt fill the background automatically.

The Qt::Color is specified as a RGB (red-green-blue) triplet, where each value is between 0 (dark) and 255 (bright). We could also have used a predefined color such as Qt::yellow instead of specifying an RGB value.

def setAngle(angle)
  if angle < 5
    angle = 5
  elsif angle > 70
    angle = 70
  end

  if @currentAngle == angle
    return
  end

  @currentAngle = angle
  update()
  emit angleChanged(@currentAngle)
end  def setAngle(degrees)

This function sets the angle value. We have chosen a legal range of 5 to 70 and adjust the given number of degrees accordingly. We have chosen not to issue a warning if the new angle is out of range.

If the new angle equals the old one, we return immediately. It is important to only emit the angleChanged() signal when the angle really has changed.

Then we set the new angle value and repaint our widget. The Qt::Widget::update() function clears the widget (usually filling it with its background color) and sends a paint event to the widget. This results in a call to the paint event function of the widget.

Finally, we emit the angleChanged() signal to tell the outside world that the angle has changed. The emit keyword is unique to Qt and not regular Ruby syntax.

def paintEvent(event)
  painter = Qt::Painter.new(self)
  painter.drawText(200, 200, tr("Angle = #{@currentAngle}"))
  painter.end()
end

This is our first attempt to write a paint event handler. The event argument contains a description of the paint event. Qt::PaintEvent contains the region in the widget that must be updated. For the time being, we will be lazy and just paint everything.

Our code displays the angle value in the widget at a fixed position. We create a Qt::Painter operating on this widget and use it to paint a string. We'll come back to Qt::Painter later; it can do a great many things.

t8.rb

angle = LCDRange.new()
angle.setRange(5, 70)

In the constructor, we create and set up the LCDRange widget. We set the LCDRange to accept angles from 5 to 70 degrees.

 cannonField = CannonField.new()

We create our CannonField widget.

connect(angle, SIGNAL('valueChanged(int)'),
        cannonField, SLOT('setAngle(int)'))
connect(cannonField, SIGNAL('angleChanged(int)'),
        angle, SLOT('setValue(int)'))

Here we connect the valueChanged() signal of the LCDRange to the setValue() slot of the CannonField. This will update CannonField's angle value whenever the user operates the LCDRange. We also make the reverse connection so that changing the angle in the CannonField will update the LCDRange value. In our example we never change the angle of the CannonField directly; but by doing the last connect() we ensure that no future changes will disrupt the synchronization between those two values.

This illustrates the power of component programming and proper encapsulation.

Notice how important it is to emit the angleChanged() signal only when the angle actually changes. If both the LCDRange and the CannonField had omitted this check, the program would have entered an infinite loop upon the first change of one of the values.

gridLayout = Qt::GridLayout.new()

So far, we have used Qt::VBoxLayout for geometry management. Now, however, we want to have a little more control over the layout, and we switch to the more powerful Qt::GridLayout class. Qt::GridLayout isn't a widget; it is a different class that can manage the children of any widget.

We don't need to specify any dimensions to the Qt::GridLayout constructor. The Qt::GridLayout will determine the number of rows and columns based on the grid cells we populate.

The diagram above shows the layout we're trying to achieve. The left side shows a schematic view of the layout; the right side is an actual screenshot of the program. (These two images are copyrighted/owned by Nokia.)

    gridLayout.addWidget(quit, 0, 0)

We add the Quit button in the top-left cell of the grid, i.e., the cell with coordinates (0, 0).

    gridLayout.addWidget(angle, 1, 0)

We put the angle LCDRange cell (1, 0).

gridLayout.addWidget(cannonField, 1, 1, 2, 1)

We let the CannonField object occupy cells (1, 1) and (2, 1).

    gridLayout.setColumnStretch(1, 10)

We tell Qt::GridLayout that the right column (column 1) is stretchable, with a stretch factor of 10. Because the left column isn't (its stretch factor is 0, the default value), Qt::GridLayout will try to let the left-hand widgets' sizes be unchanged and will resize just the CannonField when the MyWidget is resized.

In this particular example, any stretch factor greater than 0 for column 1 would have the same effect. In more complex layouts, you can use the stretch factors to tell that a particular column or row should stretch twice as fast as another by assigning appropriate stretch factors.

    angle.setValue(60)

We set an initial angle value. Note that this will trigger the connection from LCDRange to CannonField.

    angle.setFocus()

Our last action is to set angle to have keyboard focus so that keyboard input will go to the LCDRange widget by default.

Запуск приложения

When the slider is operated, the CannonField displays the new angle value. Upon resizing, CannonField is given as much space as possible.

Упражнения

Try to resize the window. What happens if you make it really narrow or really squat?

If you give the left-hand column a non-zero stretch factor, what happens when you resize the window?

Try to change "Quit" to "&Quit". How does the button's look change? ( Whether it does change or not depends on the platform.) What happens if you press Alt+Q while the program is running?

Center the text in the CannonField.