architektura komputerów

advertisement
Systemy operacyjne
Wykład 3a
Działanie aplikacji
okienkowej
dr inż. Wojciech Bieniecki
Instytut Nauk Ekonomicznych
i Informatyki
http://wbieniec.kis.p.lodz.pl/pwsz
1
Zdarzeniowy model aplikacji
System Windows bazuję na komunikatach,
które zarządzają stanem aplikacji. Komunikaty
informują aplikacje o tym co się dzieje w
systemie.
- komunikaty poleceń (ang. command
messages) - wysyłane w celu wykonania jakiejś
czynności
-komunikaty powiadomień (ang. notification
messages) - dla powiadomienia aplikacji lub
systemu o wystąpieniu zdarzenia.
Komunikaty przetwarzane są w kolejce
1. wystąpienie zdarzenia - system umieszcza je
w kolejce komunikatów,
2. process pobiera komunikat z kolejki
3. komunikat jest wysyłany pod adresem okna
docelowego,
4. jeżeli w procedurze okna, uwzględniono jego
obsługę, jest ona wywoływana, w przeciwnym
wypadku obsługa w sposób domyślny
2
Zdarzeniowy model aplikacji
Działanie aplikacji
Windows polega na
przetwarzaniu
nieskończonej pętli
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
3
Budowa komunikatu
HWND hwnd – wskaźnik na okno, do którego adresowany
jest komunikat
UINT message – określa identyfikator komunikatu
(standardowy lub własny)
WPARAM wParam, LPARAM lParam – dodatkowe parametry
komunikatu. Razem 8 bajtów. Możliwość wykorzystania
tylko kilku pierwszych bajtów (parametr wyższy).
DWORD time
Czas w momencie, którym zdarzenie miało miejsce. Pole mało istotne, rzadko używane.
POINT pt;
Pole przechowuje współrzędne kursora w momencie zaistnienia zdarzenia. To pole
także rzadko jest używane.
4
Komunikaty okna:
Komunikaty niekolejkowane
WM_CREATE -
Procedura obsługi okna otrzymuje ten komunikat podczas wykonywania
przez system operacyjny funkcji CreateWindow.
WM_DESTROY -
Komunikat informujący o tym, że system rozpoczął proces niszczenia okna
na żądanie użytkownika.
Komunikaty kolejkowane
WM_PAINT -
Większość programów pracujących pod kontrolą systemu Windows
wywołuje funkcję UpdateWindow podczas inicjalizacji w funkcji WinMain
zaraz przed uruchomieniem pętli komunikatów. W ten sposób system
wysyła do procedury okna pierwszy komunikat WM_PAINT, który
informuje procedurę okna o konieczności wypełnienia obszaru klienta.
Następnie procedura okna otrzymuje komunikat WM_PAINT zawsze
wtedy, gdy ma miejsce jedno z następujących zdarzeń:
- wcześniej zasłonięty obszar okna jest odsłaniany w wyniku zmiany
pozycji innego okna przez użytkownika;
- użytkownik zmienia rozmiar okna (jeżeli styl klasy okna posiada flagi
CS_HREDRAW i CS_VREDRAW);
program wykorzystuje funkcje ScrollWindow lub ScrollDC do
przewinięcia części jego obszaru klienckiego;
- program używa funkcji InvalidateRect lub InvalidateRgn do jawnego
wygenerowania komunikatu WM_PAINT.
5
WM_KEYDOWN WM_KEYUP
Komunikaty wysyłane do okna podczas wciskania lub zwalniania
niesystemowego przycisku klawiatury. Parametry:
- wParam określa kod jego wirtualnego przycisku.
WM_COMMAND -
Komunikat wysyłany gdy użytkownik wybiera polecenie menu lub gdy
kontrolka wysyła komunikat do okna nadrzędnego. Parametry:
- wParam (high-word) ma wartość 0 dla poleceń menu, a w przypadku
kontrolki, specyficzny dla niej kod powiadomienia;
- wParam (low-word) określa identyfikator pola menu lub kontrolki;
- lParam ma wartość 0 dla poleceń menu lub określa uchwyt okna kontrolki.
WM_LBUTTONDOWN - Komunikaty wysyłane gdy użytkownik wciska (zwalnia) lewy (prawy)
przycisk
WM_LBUTTONUP
myszy podczas, gdy kursor znajduje się w obszarze klienckim okna.
Parametry:
WM_RBUTTONDOWN - wParam określa jakie wirtualne przyciski są wciśnięte:
WM_RBUTTONUP
• MK_CONTROL – przycisk CTRL,
• MK_LBUTTON – lewy przycisk myszy,
• MK_MBUTTON – środkowy przycisk myszy,
• MK_RBUTTON – prawy przycisk myszy,
• MK_SHIFT – przycisk SHIFT;
- lParam (low-word) określa współrzędną x kursora;
- lParam (high-word) określa współrzędną y kursora.
WM_MOUSEMOVE - Komunikat wysyłany do okna podczas ruchu kursora. Parametry komunikatu
są identyczne jak w przypadku wciskania i zwalniania przycisków myszy.
6
Programowanie zdarzeniowe
Ideą programowania zdarzeniowego jest to, że zaimplementowane przez nas funkcje
nie są wywoływane jawnie w kodzie programu, lecz przez system operacyjny lub
wydzielony moduł programu. Funkcje te nazywa się zwrotnymi (callback). Jest to
nawiązanie do idei przerwań
Komunikaty:
Funkcje obsługujące tzw. kolejkę komunikatów pochodzących od systemu lub:
• mysz;
• klawiatura;
• zmiana w interfejsie graficznym programu;
• zegar;
• operacje plikowe lub sieciowe.
Wyjątki:
Funkcje obsługujące błędy czasu wykonywania mogące wystąpić w programie:
• operacje arytmetyczne (np. dzielenie przez zero);
• operacje wejścia / wyjścia (brak pliku, brak połączenia sieciowego);
• błędy adresowania pamięci (przekroczenie zakresu tablicy, nieistniejący obiekt).
Budowa aplikacji Windows
Aplikacja Windows działa w następujących fazach:
Tworzymy tzw klasę okna, definiujemy jego wygląd
wndclass.style
= CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc
= WndProc ;
wndclass.cbClsExtra
=0;
wndclass.cbWndExtra
=0;
wndclass.hInstance
= hInstance ;
wndclass.hIcon
= LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor
= LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
return 0 ;
}
Tworzymy w pamięci instancję okna
hwnd = CreateWindow (szAppName, TEXT ("The Hello Program"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
8
Budowa aplikacji Windows
Uruchamiamy nieskończoną pętlę przetwarzania komunikatów
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
Funkcja DispatchMessage uruchamia tzw funkcję okna, w której możemy obsłużyć
komunikaty
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN :
MessageBox(hwnd, “Zostal wcisniety lewy przycisk myszy…", „Informacja", MB_ICONINFORMATION | MB_OK);
return 0;
case WM_CLOSE:
if (IDCANCEL == MessageBox(hwnd, „Czy chcesz zamknac program?", „Pytanie", MB_ICONQUESTION | MB_OKCANCEL)) return 0;
else break;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
Komunikaty obsłużone przez użytkownika nie powinny trafić do domyślnej
procedury okna ‘DefWindowProc’. W tym celu po obsłużeniu komunikatu należy wyjść
z procedury zwracając wartość zero.
Wszystkie komunikaty, których nie obsłużył użytkownik, trafiają do domyślnej
9
procedury okna ‘DefWindowProc’.
Program WINAPI
Funkcja WinMain
Definiowanie klas okien programu
Rejestracja zdefiniowanych klas
Utworzenie okna głównego
Wyświetlenie okna głównego
Pętla komunikatów
Pobranie komunikatu
Przekazanie komunikatu
Procedura obsługi okna 1
Obsługa komunikatów okna 1
Reakcja systemu
na zdarzenie
System
HWND WM_CLOSE 0 0
operacyjny
HWND WM_DESTROY 0 0
Procedura obsługi okna 2
Obsługa komunikatów okna 2
Kolejka komunikatów
HWND WM_COMMAND
LPARAM
HWND WM_QUIT
0 0 WPARAM
Implementacja (Visual C++, MFC)
Architektura Dokument-Widok
Podstawowe klasy
CApplication – klasa aplikacji, uruchamiająca aplikację
CMainFrame – klasa okna głównego
CDocument – klasa dokumentu – obiekt skojarzony z otwartym
plikiem
CView – klasa widoku. Obiekt skojarzony z oknem wyświetlającym
dokument. Wizualnie – tzw obszar klienta okna – część wewnątrz
ramki
Implementacja pętli komunikatów
BEGIN_MESSAGE_MAP(CImgViewerView, CScrollView)
//{{AFX_MSG_MAP(CImgViewerView)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP()
11
Implementacja Java
W Javie i C# zaimplementowano Delegacyjny model obsługi zdarzeń
W Javie wprowadzono tu koncepcję "oddelegowania" obsługi: metoda, która
obsługuje zdarzenie jest metodą innej klasy niż obiekt, któremu zdarzenie się
przytrafiło.
Słuchacz (ang. Listener) to obiekt, który może obsługiwać zdarzenia.
Aby móc generować obiekty-Słuchacze, klasa musi implementować interfejs
nasłuchu.
Interfejs nasłuchu zawiera abstrakcyjne metody do obsługi zdarzeń.
Jego implementacja oznacza, iż metody te zyskują konkretne definicje.
Java dostarcza wiele standardowych interfejsów nasłuchu, przygotowanych do
odbioru konkretnych rodzajów zdarzeń.
Klasy-słuchacze mogą implementować jeden lub kilka z tych interfejsów, zyskując
12
w ten sposób zdolność do obsługi wybranych zestawów rodzajów zdarzeń.
Przykład - nasłuch przycisku
import javax.swing.*;
import java.awt.*;
import java.awt.event.*; // by obsługiwać zdarzenia
class Przycisk extends JFrame implements ActionListener {
JButton b1 = new JButton("Przycisk 1");
JButton b2 = new JButton("Przycisk 2");
JTextField t = new JTextField(20);
Przycisk(){
setLayout(new GridLayout(3,1));
b1.addActionListener(this);
b2.addActionListener(this);
getContentPane().add(t);
getContentPane().add(b1);
getContentPane().add(b2);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent e){
t.setText(e.getActionCommand());
}
public static void main(String[] args){
new Przycisk();
}
}
13
Działanie nasłuchu
Zdarzenie „akcja”
tworzy obiekt
ActionEvent
Interfejs nasłuchu ActionListener zawiera deklarację
void actionPerformed(ActionEvent e);
implementacja
KLIK
[ ŻRÓDŁO]
Przycisk
(JButton b)
Klasa słuchacza:
CALL
class Przyciski . . . implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Wciśnięto przycisk");
}
}
Obiekt słuchacza akcji
(może być również kontenerem przycisku)
Przyłączenie
b.addActionListener(p)
Przyciski p = new Przyciski();
14
Uwagi
1. Każdy obiekt Javy może być Słuchaczem, jeśli tylko jego klasa implementuje
jakiś zestaw interfejsów nasłuchu
2. Zdarzenia dla danego Źródła są generowane tylko wtedy, gdy do źródła
przyłączony jest Słuchacz
3. Metody obsługi zdarzeń (tu: actionPerformed) nie zwracają żadnych wyników.
4. Ze względu na strukturyzację zdarzeń i interfejsów nasłuchu kod jest odporny
na błędy: np. metoda actionPerformed musi mieć jako argument zdarzenie klasy
ActionEvent
5. Do badania właściwości zdarzenia stosuje się wyłącznie metody (w klasach
zdarzeń nic ma publicznych pól). Przykładem może być getActionCommand(),
która zwraca napis skojarzony ze zdarzeniem ACTION_PERFORMED (tu – napis na
przycisku).
6. Zdarzenia są filtrowane na niskim, ukrytym poziomie. Upraszcza to pisanie
kodu.
15
Zdarzenia fizyczne i semantyczne
java.util.EventObject
+-- java.awt.AWTEvent
+-- ActionEvent
+-- AdjustmentEvent
+-- ItemEvent
+-- TextEvent
+-- ComponentEvent
+-- ContainerEvent
+-- FocusEvent
+-- InputEvent
|
+-- KeyEvent
|
+-- MouseEvent
+-- WindowEvent
ComponentEvent -> zdarzenia fizyczne (np.
kliknięcie myszą czy zmiana rozmiaru komponentu).
Wszystkie te zdarzenia dotyczą komponentów (stąd
uszczegółowiony sposób identyfikacji źródła metoda getComponent() z klasy ComponentEvent
zwracająca referencję do obiektu klasy Component).
Zdarzenie semantyczne, mające znaczenie zależne od kontekstu:
•ActionEvent - akcja
•ItemEvent - zmiana stanu elementu wybieralnego
•AdjustmentEvent - dostosowanie obiektu dostosowywalnego
•TextEvent - zmiana tekstu obiektu tekstowego
16
Interfejsy nasłuchu zdarzeń
Klasa zdarzenia Zdarzenie – stałej identyfikującej
zdarzenie
ActionEvent
AdjustmentEve
nt
ItemEvent
TextEvent
MouseEvent
ACTION_PERFORMED
ADJUSTMENT_VALUE_CHANGED
Nazwa metody obsługi danego
zdarzenia
void metoda(Klasa_zdarzenia k)
actionPerformed
adjustmentValueChanged
ITEM_STATE_CHANGED
itemStateChanged
TEXT_VALUE_CHANGED
textValueChanged
MOUSE_ENTERED
mouseEntered
MOUSE_EXITED
mouseExited
MOUSE_PRESSED
mousePressed
MOUSE_RELEASED MOUSE_CLICKED mouseReleased
mouseClicked
MouseMotionE MOUSE_MOVED
mouseMoved
vent
MOUSE_DRAGGED
mouseDragged
KeyEvent
KEY_PRESSED
keyPressed
KEY_RELEASED
keyReleased
KEY_TYPED
keyTyped
Nasłuch
ActionListener
AdjustmentListener
ItemListener
TexlListener
MouseListener
MouseMotionListener
KeyListener
17
Interfejsy nasłuchu zdarzeń
Klasa zdarzenia
KeyEvent
Zdarzenie
KEY_PRESSED
KEY_RELEASED
KEY_TYPED
FocusEvent
FOCUS_GAINED
FOCUS_LOST
ContainerEvent COMPONENT_ADDED
COMPONENT_REMOVED
ComponentEvent COMPONENT_HIDDEN
COMPONENT_SHOWN
COMPONENT_MOVED
COMPONENT_RESIZED
WindowEvent
WINUOW_ACTIVATED
WINDOW_DEACTIVATED
WINDOW_ICONIFIED
WINDOW_DEICONIFIED
WINDOW_OPENED
WINDOW_CLOSING
WINDOW_CLOSED
Metody obsługi
Nasłuch
keyPressed
keyReleased
keyTyped
focusGained
focusLosed
componentAdded
componentRemoved
componentHidden
componentShown
componentMoved
componentResized
windowActivated
windowDeactivated
windowIconified
windowDeiconified
windowOpened
windowClosing
windowClosed
KeyListener
FocusListener
ContainerListener
ComponentListener
WindowListener
18
Implementacja Python
PyQt – sygnały i sloty
Charakterystyczną cechą Qt jest mechanizm sygnałów i slotów będący sposobem
porozumiewania się elementów aplikacji.
Sygnał emitowany jest w przypadku wystąpienia określonej akcji (np. przyciśnięto przycisk).
Slot to funkcja połączona (za pomocą QtCore.QObject.connect()) z określonym sygnałem i
jest wykonywana, gdy taki sygnał zostanie wyemitowany.
Połączenia sygnałów i slotów:
- sygnał może być połączony z wieloma slotami
- sygnał może być połączony z innym sygnałem
- slot może być połączony z wieloma sygnałami
- w PyQt sygnały są emitowane przez metodę QtCore.QObject.emit()
- połączenia mogą być bezpośrednie - synchroniczne lub kolejkowane asynchroniczne
- można tworzyć połączenia między wątkami
- sygnały są rozłączane za pomocą metody QtCore.QObject.disconnect()
19
Implementacja JavaScript
Rejestrowanie zdarzenia inline
polega na określeniu zdarzenia wewnątrz znacznika.
<a href="strona.html"
onclick="alert(' Kliknąłeś! ')">kliknij</a>
Rejestrowanie zdarzenia w sekcji skryptu (tu: z zastosowaniem funkcji anonimowej
document.getElementById('Przycisk').onclick = function() {
alert('zostałem klikniety!');
}
Rejestrowanie zdarzenia z użyciem addEventListener
var element = document.getElementById('Przycisk');
element.addEventListener('click', startDragDrop, false);
element.addEventListener('click', wypiszCos, false);
element.addEventListener('click', function()
{this.style.color = 'red'; }, false);
20
JavaScript – Kaskadowe wykonywanie zdarzeń
Załóżmy istnienie zagnieżdżonych bloków
<div style="..." id="blok1">1
<div style="..." id="blok2">2
<div style="..." id="blok3">3</div>
</div>
</div>
<script type="text/javascript">
document.getElementById('blok1').onclick = function() {
alert('Kliknąłeś mnie!')};
</script>
Kliknięcie w element wewnętrzny powoduje po wykonaniu zdarzenia przesłanie go do
elementu otaczającego (tu zawsze wywoła się alert)
Wyłączenie tego zjawiska realizujemy poprzez funkcję stopPropagation
function stopBubble(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
}
document.getElementById('blok31').onclick =
function() { alert('Kliknąłeś mnie!')}
document.getElementById('blok32').onclick =
function(e){ stopBubble(e);}
document.getElementById('blok33').onclick =
function(e){ stopBubble(e);}
21
Architektura MVC
Architektura MVC jest jedną z implementacji delegacyjnego modelu obsługi
zdarzeń. Zakłada podział składników systemu aplikacyjnego na trzy kategorie:
–Model (komponenty modelu): komponenty reprezentujące dane, na których
operują aplikacje; komponenty modelu oferują także metody dostępu do
danych
–View (komponenty prezentacji): komponenty reprezentujące wizualizację
(prezentację) danych dla użytkownika; komponenty prezentacji pobierają dane
od komponentów modelu, a następnie wyświetlają je na ekranie użytkownika
–Controller (komponenty sterujące): komponenty przechwytujące żądania
użytkowników i odwzorowujące je w wywołania metod komponentów
modelu; następnie komponenty sterujące przekazują sterowanie do
komponentów prezentacji
Implementacja JavaServlet
Przeglądarka
WWW
wysyła
sparametryzowane żądanie uruchomienia
serwletu Controller (1).
Po wywołaniu, serwlet Controller analizuje
żądanie i tworzy obiekty wymagane do
dalszego przetwarzania żądania – m.in.
obiekty klas zewnętrznych JavaBeans
realizujących funkcję Model.
W obiektach tych wywoływane są funkcje logiki biznesowej (2).
Następnie, serwlet Controller przekazuje żądanie do odpowiedniej strony Java Server Pages,
realizującej funkcję View (3).
Komponent View pobiera wyniki pracy funkcji logiki biznesowej z obiektów Model (4).
Komponent View generuje wynikowy dokument dla użytkownika (5).
23
Wzorzec MVP
Wzorzec architektury interfejsu użytkownika zarezerwowany dla
aplikacji typu Rich Client Application (lub RIA)
Interakcja View  Presenter model
• Widok i prezenter są połączone 1-1 (jeden prezenter ma jeden widok)
• Logikę obsługuje prezenter, to on rejestruje się na powiadomienia, tworzy model
• Widok ma tylko warstwę prezentacji (sterowaną przez prezenter)
• Widok jest wstrzykiwany do prezentera przez interfejs
24
Implementacja JavaFX
VIEW jest to Stage – obiekt dostarczany przez FX posiadający obiekt Scene,
będący korzeniem drzewa widoku. Scenę definiujemy przez plik fxml.
CONTROLLER – klasa implementująca interfejs Initializable.
MODEL – klasa zawierająca stan aplikacji
public class Stock {
protected String symbol;
protected String name;
public Stock(String symbol, String name) {
this.symbol = symbol;
this.name = name;
}
public String getSymbol() {
return symbol;
}
public String getName() {
return name;
}
}
25
Implementacja JavaFX
Interfejs i jego implementacja - model
public interface StockPriceUpdatedListener {
public void priceUpdated( Stock stock, double price );
}
public class StockModel implements StockPriceUpdatedListener {
/*deklaracje pól... */
public StringProperty addStock( Stock stock ) {
/*metoda dodania produktu */
}
@Override
public void priceUpdated(Stock stock, double price) {
/*metoda altualizacji produktu */
}
}
26
Implementacja JavaFX
Widok – tworzony jest poprzez SceneBuilder. Jego wynikiem jest plik FXML.
Między innymi zawiera ten plik instrukcje typu
<Button fx:id="subscribeButton" layoutX="136.0" layoutY="153.0"
mnemonicParsing="false" onAction="#subscribeButtonClicked"
text="Subscribe" />
Fragmenty klasy Kontrolera
public class StockPricePanelController implements Initializable {
@FXML
protected Button subscribeButton;
protected StockModel stockModel;
@FXML
public void subscribeButtonClicked( ActionEvent event ) {
/* metoda obsługująca kliknięcie */
}
}
27
Download