TIE-11300 Tietotekniikan vaihtuva-alainen kurssi Graafisen käyttöliittymän ohjelmointi Luento 2 Tapahtumapohjainen ohjelmointi Juha-Matti Vanhatupa
Sisältö Tapahtumapohjainen ohjelmointi Tapahtumakuuntelijoiden toteutustapoja eri ympäristöissä Käyttöliittymän komponentit
Erot perinteisiin sovelluksiin Sovelluksen kulku ei ole ennalta tiedossa. Start A B C D End Dispatcher A C B D End
Perinteinen sovellus void main(string[] args ) { string nimi, hetu, osoite; Console.WriteLine("Anna nimi: "); nimi = Console.ReadLine(); Console.WriteLine("Anna henkilötunnus: "); hetu = Console.ReadLine(); Console.WriteLine("Anna osoite: "); osoite = Console.ReadLine(); Save(nimi, hetu, osoite);
Tapahtumapohjainen sovellus string nimi, osoite, hetu; static void main() { Application.Run( new Form1() ); private void nimi_handler(string text){ nimi = text; private void hetu_handler(string text){ hetu = text; private void osoite_handler(string text){ osoite = text; private void tallenna_click(){ Save(nimi, hetu, osoite); Close();
Ohjelmoijalle Ohjelman suoritus ei etene alusta loppuun Testaaminen vaikeutuu Kaikkia ohjelman toimintoja ei välttämättä edes koskaan käytetä
Tapahtumia synnyttävät Käyttäjän toimet, hiiren- ja näppäimistön painallukset Ajastimet Käyttöjärjestelmä (Sovellus itse)
Tapahtuman kulku Event generator Event Dispatcher Handler1 Handler2 Handler n
Tapahtuma Tapahtumille ei yleensä määritellä paluuarvoa Windowsissa yleinen tapa on merkitä tapahtuman kuuntelija onetuliitteellä Kuuntelijat tietynlaisia funktioita, jotka rekisteröidään kuuntelemaan tapahtumaa Esim. QPushButton* btnok = new QPushButton(this); connect(btnok, SIGNAL(clicked()), this, SLOT(OnOKCLicked()); void OnOKClicked() { //
Tapahtumakuuntelijan toteutus QT.NET Java GTK WinApi MFC signaalien ja slottien avulla eventtien avulla rajapintojen avulla signaalien avulla sanomien avulla makrojen avulla
Qt esimerkki Tapahtumakuuntelija luodaan kytkemällä olion lähettämä signaali (esim. nappulan clicked signaali) slot-funktioon. Kytkettyjen olioiden ei tarvitse tuntea toisiaan. Signaali lähetetään kun tietty tapahtuma tapahtuu (esim. nappulaa painetaan) ja tällöin kytkettyä slotfunktiota kutsutaan. Luokkien tulee periytyä QObject luokasta (joko suoraan tai periytyen jostain QObjectin aliluokasta) Luokkien esittelyssä tulee olla Q_OBJECT makro
Qt esimerkki class MainWindow : public QMainWindow { Q_OBJECT ; public: public slots: void helloworld();
Qt esimerkki MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupui(this); connect(ui->pushbutton, SIGNAL(clicked()), this, SLOT(helloWorld())); void MainWindow::helloWorld() { ui->label->settext("hello World!");
.NET esimerkki Tapahtumakuuntelija liitetään käyttöliittymäkomponentin tapahtumaan. Tapahtumakuuntelijan liittämisessä määriteltyä funktiota kutsutaan kun tapahtuma syntyy. Liitetyn funktion parametrien tulee olla: ( object sender, Event args), ensimmäinen kertoo tapahtuman lähettävän olion, toinen sisältää eventin tiedot. Usein funktio nimetään alkamaan On-etuliitteellä.
.NET esimerkki namespace net_esim { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.button1.click += new System.EventHandler(this.button1_Click); private void button1_click(object sender, EventArgs e) { label1.text = "Hello World";
Java esimerkki Käyttöliittymäkomponenttiin liitetään tapahtumakuuntelija Tapahtumakuuntelijaluokan tulee toteuttaa ennalta määritelty kuuntelija-rajapinta (ActionListener, Mouselistener, jne.) Käyttöliittymäluokka voi toteuttaa rajapinnan (ja kuunnella tapahtumia) joko itse tai sisäluokka voi toteuttaa sen. Rajapintojen toteuttaminen saattaa aiheuttaa luokkiin tyhjiä funktioita. (Adapter:ita voidaan käyttää niiden välttämiseksi)
Java esimerkki
GTK+ esimerkki Tapahtuma on viesti X-palvelimelta. Kun käyttöliittymäkomponentti saa tapahtuman, se voi reagoida siihen lähettämällä signaalin. Signaali voidaan yhdistää callback-funktioon. Funktio suoritetaan kun signaali lähetään.
GTK+ esimerkki
WinApi Esimerkki WinApi-ohjelma sisältää sanomasilmukan, ja sanomakäsittelijänwndproc. Sanomakäsittelijässä napatun sanoman tyyppiä tarkastellaan ja sanoma käsitellään sen mukaan. Jos sanomakäsittelijässä ei reagoida sanomaan kulkee se oletuskäsittelijäfunktiolle DefWindowProc.
WinApi esimerkki int WinMain( ) { CALLBACK WndProc(HWND hwnd, UIT message, WPARAM wparam, LPARAM lparam) { HWND hwndbtn; switch( message ) { case WM_CREATE: hwndbtn = CreateWindow( button, nimi", WS_CHILD WS_VISIBLE, 0, 30, 100, 30, hwnd, (HMENU) 1, hinst, NULL); break; case WM_COMMAND: if ( LOWORD(wParam) == BN_CLICKED ) { if( HIWORD(wParam) == hwndbtn ) { //
MFC esimerkki Toteutus makroilla Message map -taulu yhdistää viestejä ja jäsenfunktioita. Jokainen luokka, joka periytyy CCmdTargetluokasta voi toteuttaa oman message map:n.
MFC esimerkki // CMyDlg.h class CMyDlg : public CDialog { afx_msg void OnBnClick() DECLARE_MESSAGE_MAP() // CMyDlg.cpp BEGIN_MESSAGE_MAP( CMyDlg, CDialog ) ON_BN_CLICKED(IDC_BTN, &CMyDlg::OnBnClicked) END_MESSAGE_MAP() void CMyDlg::OnBnClick () { //
Android esimerkki Androidia ohjelmoidaan Javalla, joten toteutus hyvin samankaltainen kuin normaalin Javan. Tapahtumankäsittelijämetodi OnClickListener() rekisteröidään nappulaan. final Button button = (Button) findviewbyid(r.id.button_id); button.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { // Perform action on click );
Android esimerkki Käytössä myös oikopolku jos tarvitaan vain perustoiminnallisuus XML tiedostossa määritellään tapahtuma käyttöliittymäkomponenttiin esim. nappulaan <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onclick="buttonclick" android:text="click me" /> Ja tapahtumankäsittelijäfunktio, jota kutsutaan public void buttonclick(view view) {
Windows Phone 8 Ohjelmointikielinä C# tai Visual Basic Tapa 1: XAML <Button Content="XAML" Height="72" HorizontalAlignment="Left" Margin="12,63,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" /> Tapa 2: C# koodista button1.click += new RoutedEventHandler(button1_Click); Ja tapahtumankäsittelijäfunktio void button1_click(object sender, RoutedEventArgs e) { textblock1.text = "Hello World!";
Pääikkuna (top-level window) Jokaisella sovelluksella on pääikkuna (main window), joka luodaan ohjelman käynnistyessä. Esim. Qt:ssä mainfunktiossa. Pääikkuna sisältää yleensä otsikon, valikon, reunukset sekä minimize- ja maximize-painikkeet Kun pääikkuna suljetaan, myös sovelluksen suoritus päättyy Pääikkunalla ei ole parent-ikkunaa
Pääikkuna (top-level window)
Lapsikkuna (child window) Lapsi-ikkunalla (child window) on aina yksi isäntäikkuna (parent window) Paikkakoordinaatit suhteessa isäntäikkunaan Lapsi-ikkuna ei voi sijaita isäntäikkunan ulkopuolella
Dialogit Modaalinen vs modaaliton ikkuna Omistus-suhde (owner) Tarkoitettu lyhytaikaiseen kommunikaatioon käyttäjän kanssa Tyypillisesti ei menua, eikä minimize, mazimize painikkeita.
Z-järjestys
Ikkunan fokus Valittuna oleva ikkuna (kuvassa btn1) Komponentti, joka ottaa vastaan näppäinpainallukset jne. Käyttäjä voi vaihtaa focusta esim hiiren tai näppäimistön avulla
Tab-järjestys Tabulaattori-näppäimellä voidaan vaihtaa focus seuraavalle (lapsi)ikkunalle Suunnittele käyttöliittymä aina siten että sitä voidaan käyttää pelkän näppäimistönkin avulla! Tab