Kehitys/Oppikurssit/Qt4 Ruby -oppikurssi/Kappale 08

    From KDE TechBase
    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ä. 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 asettaa 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, että mikä tekee sen mahdolliseksi on paletin asettaminen (katso seuraava lohko). 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, että 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 luomme 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)'))
    

    Tässä yhdistämme LCDRange-komponentin valueChanged()-signaalin CannonField-kentän setValue()-väliin . Tämä päivittää CannonField-kentän kulma-arvon joka kerran, kun käyttäjä käyttää LCDRange-komponenttia. Teemme myös päinvastaisen yhteyden niin että kulman muuttuminen CannonField-kentässä päivittää LCDRange-arvon. Esimerkissämme emme koskaan muuta CannonField-kulmaa suoraan; mutta tekemällä viimeisen connect() varmistamme, että mikään tulevaisuuden muutos ei häiritse noiden kahden arvon välistä synkronointia.

    Tämä kuvaa komponenttiohjelmoinnin voimaa ja oikeaa kapselointia.

    Huomaa kuinka on tärkeää lähettää angleChanged()-signaali vain silloin kun kulma todella muuttuu. Jos sekä LCDRange että CannonField ohittivat tämän tarkistuksen, ohjelma siirtyisi päättymättömään silmukkaan toisen arvon ensimmäisellä muutoksella.

    gridLayout = Qt::GridLayout.new()
    

    Toistaiseksi olemme käyttäneet Qt::VBoxLayout-luokkaa ulkoasuhallintaan. Nyt haluamme kuintenkin hallita ulkoasua paremmin, ja vaihdamme tehokkaampaan Qt::GridLayout-luokkaan. Qt::GridLayout ei ole käyttöliittymäkomponenttit; se on erilainen luokka, joka hallitsee minkä tahansa käyttöliittymäkomponentin lapsia.

    Meidän ei tarvitse määritellä mitään mittoja Qt::GridLayout-konstruktorille. Luokka Qt::GridLayout määrittelee rivien ja sarakkeiden lukumäärän niiden rasterisolujen perusteella, jotka otamme käyttöön.

    Yllä oleva kuva näyttää asettelun, jonka yritämme saavuttaa. Vasen puoli näyttää asettelun kytkentäkaaviopuolen; oikea puoli on ohjelman todellinen näytönkaappaus. (Nokia omistaa näiden kahden kuvan copyright-oikeudet.)

        gridLayout.addWidget(quit, 0, 0)
    

    Lisäämme Quit-painikkeen rasterin vasempaan yläkulmaan, ts. solun koordinaatit ovat (0, 0).

        gridLayout.addWidget(angle, 1, 0)
    

    Laitamme kulman LCDRange soluun (1, 0).

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

    Annamme CannonField'-objektin varata solut (1, 1) ja (2, 1).

        gridLayout.setColumnStretch(1, 10)
    

    Kerromme Qt::GridLayout-luokalle, että oikea sarake (sarake 1) on venytettävä, venytyskertoimella 10. Koska vasen sarake ei ole venytettävä (sen venytyskerroin on 0, oletusarvo), Qt::GridLayout-luokka yrittää pitää vasemmanpuoleisen käyttöliittymäkomponentin koon muuttumattomana ja muuttaa vain CannonField-kenttää kun MyWidget-käyttöliittymäkomponentin koko muuttuu.

    Tässä erityisessä esimerkissä mikä tahansa venytyskerroin, joka on suurempi kuin 0 sarakkeessa 1 aiheuttaisi saman vaikutuksen. Mutkikkaammissa asetteluissa voimme käyttää venytyskerrointa kertomaan, että tietty sarake tai rivi pitäisi venyttää kaksi kertaan niin nopeasti kuin joku toinen asettamalla sopivat venytyskertoimet.

        angle.setValue(60)
    

    Asetamme alustuskulma-arvon. Huomaa, että tämä liipaisee yhteyden LCDRange-komponentista CannonField-kenttään.

        angle.setFocus()
    

    Viimeinen toimemme on asettaa angle-näppäimistökohdistus niin että näppäimistösyöttö menee oletuksena LCDRange-käyttöliittymäkomponentille.

    Sovelluksen suorittaminen

    Kun liukukytkintä käytetään CannonField näyttää uuden kulma-arvon. Kokoa muutettaessa CannonField-kentälle annetaan tilaa niin paljon kuin on mahdollista.

    Harjoitukset

    Yritä muuttaa ikkuna kokoa. Mitä tapahtuu, jos yrität tehdä siitä todella kapean tai matalan?

    Mitä tapahtuu ikkunan kokoa muutettaessa, jos annat vasemmanpuoleiselle sarakkeelle venytyskertoimen nollasta poikkeavan arvon?

    Yritä muuttaa tekstiä "Quit" tekstiksi "&Quit". Kuinka painikkeen ulkoasu muuttuu? (Ulkoasun mahdollinen muuttuminen riippuu käyttöjärjestelmäalustasta.) Mitä tapahtuu, jos painat näppäimiä Alt+Q ohjelman suorituksen aikana?

    Keskitä teksti CannonField-kentässä.