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

From KDE TechBase
(Created page with "Tämä kuvaa komponenttiohjelmoinnin voimaa ja oikeaa kapselointia.")
(Created page with "=== Sovelluksen suorittaminen ===")
Line 179: Line 179:
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 ===
=== Sovelluksen suorittaminen ===


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.

Revision as of 17:59, 26 September 2011

Other languages:


Development/Tutorials/Qt4 Ruby Tutorial/Chapter 08


Valmistaudutaan taisteluun
Tutorial Series   Qt4 Ruby -oppikurssi
Previous   Oppikurssi 7 - Yksi asia johtaa toiseen
What's Next   Oppikurssi 9 - Kanuunalla kykenet
Further Reading   n/a

Valmistaudutaan taisteluun

Tiedostot:

Yleistä

Tässä esimerkissä esittelemme ensimmäisen räätälöidyn käyttöliittymäkomponentin, joka voi piirtää itsensä. Me lisäämme myös hyödyllisen näppäimistörajapinnan (kahdella koodirivillä).

Läpikäynti rivi riviltä

lcdrange.rb

Tämä tiedosto on hyvin samanlainen kuin lcdrange.rb Kappaleessa 7. Olemme lisänneet yhden välin: setRange().

Lisäämme nyt mahdollisuuden asettaa LCDRange-lukualueen. Tähän asti se on ollut kiinteä välillä 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()-väli asetaa liukukytkimen lukualueen LCDRange. Koska olemme asettaneet QLCDNumber näyttämään aina kaksi numeroa, haluamme rajoittaa mahdollisen lukualueen minVal ja maxVal ylivuoden välttämiseksi käyttöliittymäkomponentissa QLCDNumber. (Meillä olisi lupa laskea arvot aina arvoon -9, mutta valitsemme olemaan tekemättä sitä.) Jos argumentit ovat virheellisiä, käytämme Qt:n QtGlobal::qWarning()-funktiota julkaisemaan varoituksen käytäjälle ja palaamme välittömästi. QtGlobal::qWarning() on printf-tyyppinen funktio, joka oletuksena lähettää tulosteensa kohteeseen $stderr. Jos haluat, voit asentaa oman käsittelijäfunktion käyttäen QtGlobal::qInstallMsgHandler()-objektia.

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

Tämä saa nestekidenumeromme näyttämään paremmilta. En ole varma, mutta uskon, mikä tekee mahdolliseksi on paletin asettaminne (katso seuraava lohko). Mitä tiedän nyt, on että tällä rivillä ei ollut vaikutusta kun sitä yritettiin edellisessä kappaleessa, mutta se toimii tässä.

cannon.rb

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

Konstruktori alustaa kulma-arvon 45 asteeseen ja asettaa räätälöidyn paletin tälle käyttöliittymäkomponentille.

Tämä paletti käyttää indikoitua väriä taustana ja poimii muita värejä siihen sopivasti. (Tälle käyttöliittymäkomponentille käytetään todellisuudessa vain tausta- ja tekstivärit). Kutsumme sitten setAutoFillBackground(true) kertomaan Qt:lle, etä taustan voi täyttää automaattisesti.

Qt::Color määriteltiin RGB (punainen-vihreä-sininen) -triplettinä, missä jokainen arvo on välillä 0 (tumma) ja 255 (kirkas). Voisimme myös käyttää ennakolta määriteltyjä värejä kuten Qt::yellow eikä RGB-arvojen määrittelyjä.

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)

Tämä funktio asettaa kulma-arvon. Olemme valinneet lailliseksi arvoalueeksi 5 ... 70 ja säätäneet annetun asetenumeron sen mukaan. Olemme valinneet, että emme julkaise varoitusta, jos uusi kulma on lukualueen ulkopuolella.

Jos uusi kulma on sama kuin vanha, palaamme välittömästi. On tärkeää lähettää vain angleChanged()-signaali kun kulma on todella muuttunut.

Sitten asetamme uuden kulman ja piirrämme käyttöliittymäkomponenttimme. Qt::Widget::update()-funktio tyhjentää käyttöliittymäkomponentin (tavallisesti täyttämällä sen taustavärin) ja asettaa piirtämistapahtuman käyttöliittymäkomponentille. Tästä seuraa kutsu käyttöliittymäkomponentin piirtämistapahtumafunktiolle.

Lopuksi lähetämme angleChanged()-signaalin kertomaan ulkopuoliselle maailmalle, että kulma on muuttunut. Avainsana emit on ainutlaatuinen Qt:lle ja ei ole säännöllistä Ruby-syntaksia.

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

Tämä oli ensimmäinen yrityksemme kirjoittaa piirtämistapahtumakäsittelijä. Tapahtuma.argumentti sisältää piirtämistapahtuman kuvauksen. Qt::PaintEvent sisältää käyttöliittymäkomponentin alueen, joka on päivitettävä. Toistaiseksi olemma laiskoja ja piirrämme kaikki.

Koodimme näyttää kulma-arvon käyttöliittymäkomponentissa kiinteässä paikassa. Luomme Qt::Painter-toiminnon tälle käyttöliittymäkomponentille ja käytämme sitä merkkijonon piirtämiseen. Palaamme Qt::Painter-toimintoon myöhemmin; se voi tehdä todella monia asioita.

t8.rb

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

Konstruktorissa lumme ja asetamme LCDRange-käyttöliittymäkomponentin. Asetamme LCDRange-komponentin hyväksymään kulmat välillä 5 ... 70 astetta.

 cannonField = CannonField.new()

Luomme CannonField-käyttöliittymäkomponentin.

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.

Tämä kuvaa komponenttiohjelmoinnin voimaa ja oikeaa kapselointia.

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.

Sovelluksen suorittaminen

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

Exercises

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.