Wprowadzenie do platformy Qt

Podobne dokumenty
Programowanie w środowiskach RAD Qt i C++

Programowanie w środowiskach RAD QtCreator, Qt i C++

Qt sygnały i designer

Wprowadzenie do tworzenia zaawansowanych interfejsów graficznych: QGraphics View Framework vs. QML. Jakub Bogacz. Patryk Górniak

Podstawy programowania w Qt4

Qt - podstawowe cechy

Signals + Threads: Qt vs. Boost

Oprogramowanie i wykorzystanie stacji roboczych. Wykład 4

Implementacja aplikacji sieciowych z wykorzystaniem środowiska Qt

Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC.

Praca z aplikacją designer

Qt - dialogi. Bogdan Kreczmer. ZPCiR ICT PWR pokój 307 budynek C3

The Graphics View Framework. Oprogramowanie i wykorzystanie stacji roboczych. Wykład 5. he Graphics View Framework - architektura

Zaawansowane programowanie w języku C++ Klasy w C++

Wykład 4: Klasy i Metody

Laboratorium 1 Temat: Przygotowanie środowiska programistycznego. Poznanie edytora. Kompilacja i uruchomienie prostych programów przykładowych.

Programowanie obiektowe

Mechanizm sygnałów i slotów. Oprogramowanie i wykorzystanie stacji roboczych. Wykład 4. Sygnały i sloty - definicja klasy

Qt sygnały i sloty. Bogdan Kreczmer. Katedra Cybernetyki i Robotyki Wydział Elektroniki Politechnika Wrocławska

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

X Window System (I) Oprogramowanie i wykorzystanie stacji roboczych. Wykład 1. X Window System (III) X Window System (II)

Qt in Education. Sieć I drukowanie

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Programowanie obiektowe

PHP 5 język obiektowy

Zaawansowane programowanie w C++

Zaawansowane programowanie w języku C++ Programowanie obiektowe

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Kurs WWW. Paweł Rajba.

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

System X Window (I) Oprogramowanie i wykorzystanie stacji roboczych. Wykład 3. System X Window (III) System X Window (II)

Oprogramowanie i wykorzystanie stacji roboczych. Wykład 3

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Szablony funkcji i szablony klas

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

PARADYGMATY PROGRAMOWANIA Wykład 4

Programowanie, część III

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Programowanie obiektowe

Materiał. Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Funkcje Wskaźniki Referencje Tablice dynamiczne Typ string Przeładowania funkcji

Wykład 3 Składnia języka C# (cz. 2)

Wstęp do programowania obiektowego. Wykład 2

Qt hierarchia elementów graficznych

Delphi podstawy programowania. Środowisko Delphi

Rys. 1. Główne okno programu QT Creator. Na rysunku 2 oznaczone zostały cztery przyciski, odpowiadają kolejno następującym funkcjom:

Programowanie obiektowe

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

C# 6.0 : kompletny przewodnik dla praktyków / Mark Michaelis, Eric Lippert. Gliwice, cop Spis treści

Multimedia JAVA. Historia

Projektowanie obiektowe. Roman Simiński Wzorce projektowe Wybrane wzorce strukturalne

Programowanie aplikacji na iphone. Wstęp do platformy ios. Łukasz Zieliński

Szablony klas, zastosowanie szablonów w programach

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Wprowadzenie do szablonów szablony funkcji

Współbieżność w środowisku Java

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Wprowadzenie do szablonów szablony funkcji

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

Metaprogramowanie w Ruby

Zofia Kruczkiewicz, ETE8305_2 1

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Podstawy programowania III

Podstawy Programowania Obiektowego

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Programowanie Komputerów

Rozdział 4 KLASY, OBIEKTY, METODY

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Programowanie 2. Język C++. Wykład 3.

Programowanie w języku C++

Instrukcja laboratoryjna cz.3

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Wyjątki (exceptions)

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

1 Atrybuty i metody klasowe

Temat 1. Podstawy Środowiska Xcode i wprowadzenie do języka Objective-C

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Aplikacja po polsku. Bogdan Kreczmer. ZPCiR ICT PWr pokój 307 budynek C3.

Wykład 8: klasy cz. 4

Aplikacje w środowisku Java

Dziedziczenie. Tomasz Borzyszkowski

Dokumentacja do API Javy.

Definicje klas i obiektów. Tomasz Borzyszkowski

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

Wykład 1. Program przedmiotu. Programowanie (język C++) Literatura. Program przedmiotu c.d.:

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Programowanie obiektowe i C++ dla matematyków

PRZYJĘTE ZASADY GRY...3 ZAŁOŻENIA PROJEKTU...4 CZYM JEST I DLACZEGO QT?...5 DIAGRAM KLAS...7 DIAGRAM GRY SINGLE PLAYER...8 DIAGRAM MULTIPLAYERA...

Transkrypt:

Wprowadzenie do platformy Qt

Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC. 2012 Digia Plc. The enclosed Qt Materials are provided under the Creative Commons Attribution-Share Alike 2.5 License Agreement. The full license text is available here: http://creativecommons.org/licenses/by-sa/2.5/legalcode. Digia, Qt and the Digia and Qt logos are the registered trademarks of Digia Plc. in Finland and other countries worldwide.

Qt jest platformą do tworzenia programów wieloplatformowych, napisaną w C++. Oprócz języka C++ dostępne są wiązania (ang. bindings) dla innych języków Python, Ruby, C#, etc. Oryginalnie dla graficznych interfejsów użytkownika obecnie do wszystkiego Bazy danych, XML, WebKit, multimedia, sieć, OpenGL, skrypty, bez-gui...

Qt składa się z modułów Wszystkie moduły posiadają podobny schemat i API QtCore QtGui QtOpenGL QtOpenVG QtSvg QtWebKit QtSql QtNetwork QtScript QtXml QtXmlPatterns Phonon QtMultimedia

Qt rozszerza C++ m.in. dzięki wykorzystaniu makr i introspekcji foreach (int value, intlist) { } QObject *o = new QPushButton; o->metaobject()->classname(); // returns QPushButton connect(button, SIGNAL(clicked()), window, SLOT(close())); Kod to nadal zwykły C++

Wieloplatformowe aplikacje budowane na podstawie jednego kodu źródłowego Aplikacje natywne z odpowiednim wyglądem i sposobem funkcjonowania Łatwe do (ponownego) wykorzystania API, wysoka produktywność, otwartość

Windows 10+ Mac OS X Linux/Unix X11

Android ios Windows Phone Platformy wbudowane

#include <QApplication> #include <QLabel> int main( int argc, char **argv ) { QApplication app( argc, argv ); QLabel l( "Hello World!" ); l.show(); return app.exec(); }

#include <QApplication> #include <QLabel> int main( int argc, char **argv ) { QApplication app( argc, argv ); QLabel l( "Hello World!" ); l.show(); return app.exec(); }

#include <QApplication> #include <QLabel> int main( int argc, char **argv ) { QApplication app( argc, argv ); QLabel l( "Hello World!" ); l.show(); return app.exec(); }

Dlaczego wykorzystuje się to: int main(int argc, char *argv[]) Zamiast tego? int main() Parametry argc oraz argv pozwalają na odczyt wartości podanych z linii komend Gdy wykorzystywana jest wersja bezparametrowa funkcji main(), te wartości są pomijane

#include <QApplication> #include <QLabel> int main( int argc, char **argv ) { QApplication app( argc, argv ); QLabel l( "Hello World!" ); l.show(); return app.exec(); }

#include <QApplication> #include <QLabel> int main( int argc, char **argv ) { QApplication app( argc, argv ); QLabel l( "Hello World!" ); l.show(); return app.exec(); }

LGPL za darmo Aplikacja może być otwarto-źródłowa bądź zamknięta Zmiany dokonane w Qt muszą zostać dostarczone społeczności GPL za darmo Aplikacja musi być otwarto-źródłowa Zmiany dokonane w Qt muszą zostać dostarczone społeczności Commercial trzeba płacić Aplikacja może być zamknięta Nie trzeba przekazywać społeczności dokonanych zmian w Qt

1991 Haavard Nord oraz Eirik Chambe- Eng rozpoczynają pracę nad Qt wspierającą X11 oraz Windows 1994 Powstanie firmy Trolltech 1996 Powstaje projekt KDE prowadzony przez Matthias a Ettrich a

2001 Dodano wsparcie Mac OS X 2005 Wszystkie platformy wydano pod licencją GPL 2008 Nokia przejmuje Trolltech 2009 Wsparcie dla S60 2000 Wsparcie dla MeeGo 2012 Digia przejmuje platformę Qt 2014 Powstaje Qt Company 2016 Qt wersja 5.6

Aktualnie rozwój Qt jest nadzorowany przez firmę Qt Company: http://www.qt.io/ Podstawowe źródło informacji oraz możliwość pobrania: http://www.qt.io/download/

QtCreator główne środowisko IDE. QtAssistant narzędzie wspomagające pracę z podpowiedziami i dokumentację, zintegrowane z QtCreator'em. QtDesigner narzędzie typu RAD, pozwalające na tworzenie warstwy wizualnej aplikacji, zintegrowane z QtCreator'em. QtLinguist narzędzie do przygotowywania różnych wersji językowych dla tworzonych programów. Konsola narzędzie typu commandline z odpowiednio ustalonymi zmiennymi środowiskowymi.

QObject jest klasą bazową dla niemalże wszystkich klas i widgetów Qt Implementuje wiele mechanizmów, które stanowią podstawę Qt zdarzenia sygnały i sloty właściwości zarządzanie pamięcią

QObject jest klasą bazową dla wielu klasy platformy Qt. Zdarzają się jednak wyjątki w następującej postaci: Klasy, które muszą posiadać minimalny narzut obliczeniowy jak te realizujące podstawowe operacje graficzne Kontenery danych (QString, QList, QChar, itp) Klasy, które mogą być kopiowane, ponieważ QObjectów nie da się skopiować.

Każda instancja klasy QObject jest samodzielnym indywiduum! Zwykle posiadają swoją nazwę (QObject::objectName) Mają określone miejsce w hierarchii innych instancji QObject Mogą mieć powiązania z innymi instancjami QObject

Qt implementuje mechanizm introspekcji w C++ Każdy QObject posiada meta object Meta object posiada informacje o: Nazwie klasy (QObject::className) Dziedziczeniu (QObject::inherits) Właściwościach Sygnałach i slotach Ogólnie o klasie (QObject::classInfo)

Metadane są generowane podczas procesu kompilacji przez tzw. meta object compiler (moc). Standardowy proces kompilacji i budowania w C++ źródła *.cpp kompilacja pliki obiektowe *.o łączenie pliki wykonywane dodawanie pliki nagłówkowe *.h

Metadane są generowane podczas procesu kompilacji przez tzw. meta object compiler (moc). Standardowy proces kompilacji i budowania w Qt źródła *.cpp kompilacja pliki obiektowe *.o łączenie pliki wykonywalne includes kompilacja pliki nagłówkowe moc wygenerowane moc_*.cpp *.h Moc generuje dane wykorzystując pliki nagłówkowe.

Czego szuka moc? Makro Q_OBJECT class MyClass : public QObject { Q_OBJECT Q_CLASSINFO("author", "John Doe") public: MyClass(const Foo &foo, QObject *parent=0); Foo foo() const; QObject musi być dziedziczony jako pierwszy Ogólnie informacje o klasie public slots: void setfoo( const Foo &foo ); signals: void foochanged( Foo ); Słowa kluczowe Qt private: Foo m_foo; };

Klasy mają wiedzę o sobie (w czasie wykonywania programu) if (object->inherits("qabstractitemview")) { Umożliwia dynamiczne rzutowanie bez RTTI QAbstractItemView *view = static_cast<qabstractitemview*>(widget); view->... enum CapitalsEnum { Oslo, Helsinki, Stockholm, Copenhagen }; int index = object->metaobject()->indexofenumerator("capitalsenum"); object->metaobject()->enumerator(index)->key(object->capital()); Meta obiekt przechowuje szczegóły Umożliwia konwersję wartości wyliczeniowych na ciągi znaków Jest to przydane w przypadku łączenia Qt z językami skryptowymi

QObject posiada właściwości realizowane przez metody akcesora i modyfikatora Modyfikator, zwraca void, posiada jeden argument class QLabel : public QFrame { Q_OBJECT Q_PROPERTY(QString text READ text WRITE settext) public: QString text() const; public slots: void settext(const QString &); }; Akcesor, const, zwraca wartość, brak argumentów Konwencja nazewnictwa: color, setcolor Dla wartości boolowskich: isenabled, setenabled

Po co stosować hermetyzację? Można sprawdzić poprawność wartości: void setmin( int newmin ) { if( newmin > m_max ) { qwarning("ignoring setmin(%d) as min > max.", newmin); return; }... Można reagować na zmiany (aktualizując elementy zależne od wartości danego atrybutu) void setmin( int newmin ) {... } m_min = newmin; updateminimum();

Dlaczego akcesory? Separacja użytych struktur danych QSize size() const { return m_size; } int width() const { return m_size.width(); }

Q_PROPERTY(type name READ getfunction [WRITE setfunction] [RESET resetfunction] [NOTIFY notifysignal] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])

Dostęp bezpośredni QString text = label->text(); label->settext("hello World!"); Dostęp przez warstwę metadanych QString text = object->property("text").tostring(); object->setproperty("text", "Hello World"); Odpytywanie o właściwości w czasie wykonania int QMetaObject::propertyCount(); QMetaProperty QMetaObject::property(i); QMetaProperty::name/isConstant/isDesignable/read/write/...

Mechanizm ten może być wykorzystywany by dodawać właściwości w czasie działania programu: bool ret = object->setproperty(name, value); prawda jeżeli właściwość jest już zdefiniowana przez makro Q_PROPERTY QObject::dynamicPropertyNames() const fałsz jeśli dodajemy dynamicznie zwraca listę właściwości dynamicznie dodanych Może być używany np. do oznaczania obiektów

Makro opisujące właściwości class AngleObject : public QObject { Q_OBJECT Q_PROPERTY(qreal angle READ angle WRITE setangle) public: AngleObject(qreal angle, QObject *parent = 0); qreal angle() const; void setangle(qreal); private: qreal m_angle; }; Stan obiektu Modyfikator Akcesor Wartość początkowa

AngleObject::AngleObject(qreal angle, QObject *parent) : QObject(parent), m_angle(angle) Wartość { początkowa } qreal AngleObject::angle() const { return m_angle; } void AngleObject::setAngle(qreal angle) { } m_angle = angle; dosomething(); Zmień stan i zareaguj na zmianę. Akcesor po prostu zwraca wartość (ale może wykonywać bardziej skomplikowane obliczenia)

Standardowa deklaracja typu wyliczeniowego. Informacja, że class AngleObject : public QObject AngleMode { jest typem wyliczeniowym. Q_OBJECT Q_ENUMS(AngleMode) Q_PROPERTY(AngleMode anglemode READ...) Właściwość public: wykorzystująca enum AngleMode {Radians, Degrees}; typ wylizceniowy };...

QObject can have parent and children When a parent object is deleted, it deletes its children QObject *parent = new QObject(); QObject *child1 = new QObject(parent); QObject *child2 = new QObject(parent); QObject *child1_1 = new QObject(child1); QObject *child1_2 = new QObject(child1); delete parent; parent parent deletes child1 and child2 child1 deletes child1_1 and child1_2 child1 child1_1 child2 child1_2

Hierarchia rodzic-dziecko QDialog *parent = new QDialog(); QGroupBox *box = new QGroupBox(parent); QPushButton *button = new QPushButton(parent); QRadioButton *option1 = new QRadioButton(box); QRadioButton *option2 = new QRadioButton(box); delete parent; Parent usuwa box oraz button box usuwa option1 oraz option2

Wskaźnik this jako odwołanie do rodzica Dialog::Dialog(QWidget *parent) : QDialog(parent) { QGroupBox *box = QGroupBox(this); QPushButton *button = QPushButton(this); QRadioButton *option1 = QRadioButton(box); QRadioButton *option2 = QRadioButton(box);... Alokacja rodzica na stosie: void Widget::showDialog() { Dialog dialog; } if (dialog.exec() == QDialog::Accepted) {... } dialog jest w tym momencie usuwany

Używając new oraz delete, pamięć alokowana jest na stercie. Odpowiedni obszar sterty musi być zwolniony używając delete żeby zapobiec wyciekom pamięci. Obiekty alokowane na stercie żyją tak długo jak są potrzebne. new Tworzenie Usuwanie delete

Zmienne lokalne alokowane są na stosie. Zmienne alokowane na stosie są automatycznie usuwane gdy utracą widoczność. Podobna sytuacja ma miejsce w przypadku obiektów. int a Tworzenie Usuwanie }

By zrealizować automatyczne zarządzanie pamięcią, wystarczy by węzły rodziców były alokowane na stosie MyMainWindow QApplication int main(int argc, char **argv) { QApplication a(argc, argv); MyMainWindow w; w.show(); return a.exec(); } MyMainWindow::MyMainWindow(... { new QLabel(this); new... }

Obiekty klasy QObject mogą mieć zmienianego właściciela: obj->setparent(newparent); Rodzice są informowani o tym, kiedy obiekt jest usuwany: delete listwidget->item(0); // Usuwa pierwszy element (uwaga) Lepiej wykorzystać metody z przedrostkiem take : QLayoutItem *QLayout::takeAt(int); QListWidgetItem *QListWidget::takeItem(int); // Safe alternative QListWidgetItem *item = listwidget->takeitem(0); if (item) { delete item; }

Większość QObjectów pozwala na ustawienie rodzica, z domyślną wartością 0 (null) QObject(QObject *parent=0); Rodzicem Qwidget jest inny QWidget Zwykle dostępnych jest wiele różnych konstruktorów (w tym jeden pozwalający zdefiniować wyłącznie rodzica) QPushButton(QWidget *parent=0); QPushButton(const QString &text, QWidget *parent=0); QPushButton(const QIcon &icon, const QString &text, QWidget *parent=0); Rodzic jest zwykle pierwszy na liście argumentów QLabel(const QString &text, QWidget *parent=0, Qt::WindowFlags f=0);

Wykorzystanie slotu jako funkcji przypisanej do sygnału rozwiązane analogiczne do procedury obsługi zdarzenia (VS)

Wykorzystanie powiązania sygnał-slot rozwiązane z wykorzystaniem QtDesigner'a.

Informacje o powiązaniu sygnał-slot zapisywane są do pliku definicji okna głównego mainwindow.ui.

Wykorzystanie powiązania sygnał-slot wykorzystaniem makra connect w kodzie programu.

Wywołanie zwrotne to wskaźnik do funkcji, która jest wywoływana kiedy nastąpi dane zdarzenie. Każda funkcja może być przyporządkowania do wywołania zwrotnego. Brak bezpieczeństwa typów (type-safety) Zawsze działa jako bezpośrednie wywołanie. Mechanizm slotów i sygnałów jest bardziej dynamiczny: Bardziej ogólny mechanizm Dokonywane jest sprawdzenie typów Łatwiejsza implementacja wielowątkowości

Slot definiowany jest w sekcji slots public slots: void apublicslot(); protected slots: void aprotectedslot(); private slots: void aprivateslot(); Slot może zwracać wartości, ale nie podczas tworzenia połączeń Dowolna liczba sygnałów może być połączona ze slotem connect(src, SIGNAL(sig()), dest, SLOT(slt())); Implementacja nie różni się od zwyczajnej funkcji Może być wywoływana jak zwyczajna funkcja

Sygnał definiowany jest w sekcji signals: signals: void asignal(); Sygnał nie może zwracać jakiejkolwiek wartości (void) Sygnał nie może posiadać implementacji Moc zapewnia jego implementację Sygnał może zostać podłączony do dowolnej liczby slotów Zwykle realizowany jest jako połączenie bezpośrednie, ale może być wykorzystywany również przy odwoływaniu się do różnych wątków czy komunikacji sieciowej Sloty wywoływane są w losowej kolejności Sygnał emitowany jest korzystając ze słowa kluczowego emit emit asignal();

QObject* QObject::connect( src, SIGNAL( signature ), dest, SLOT( signature ) ); <function name> ( <arg type>... ) Sygnatura składa się z nazwy funkcji i typów argumentów. Nazwy zmiennych bądź ich wartości nie są dozwolone. clicked() settitle(qstring text) toggled(bool) setvalue(42) settext(qstring) setitem(itemclass) textchanged(qstring) rangechanged(int,int)

Qt może ignorować argumenty, ale nie może ich tworzyć z niczego Sygnał rangechanged(int,int) rangechanged(int,int) rangechanged(int,int) Slot setrange(int,int) setvalue(int) updatedialog() valuechanged(int) valuechanged(int) valuechanged(int) setrange(int,int) setvalue(int) updatedialog() textchanged(qstring) setvalue(int) clicked() clicked() setvalue(int) updatedialog()

Połączenie dwustronne connect(dial1, SIGNAL(valueChanged(int)), dial2, SLOT(setValue(int))); connect(dial2, SIGNAL(valueChanged(int)), dial1, SLOT(setValue(int))); Stop pętli nieskończonej sygnał nie jest emitowany, jeżeli nie nastąpiła zmiana wartości void QDial::setValue(int v) { if(v==m_value) return;... Nie należy o tym zapominać!

class AngleObject : public QObject { Q_OBJECT Q_PROPERTY(qreal angle READ angle WRITE setangle NOTIFY anglechanged) public: AngleObject(qreal angle, QObject *parent = 0); qreal angle() const; Jaki sygnał wysłać jak się zmieni? public slots: void setangle(qreal); signals: void anglechanged(qreal); private: qreal m_angle; }; Modyfikatory to często sloty. Sygnały to często akcesory

void AngleObject::setAngle(qreal angle) { if(m_angle == angle) return; } m_angle = angle; emit anglechanged(m_angle); Zabezpieczenie przez pętlą nieskończoną. Nie należy zapomnieć! Uaktualnij stan i wyślij odpowiedni sygnał Sygnały najczęściej definiowane są w sekcji chronionej (protected) dlatego można je wysyłać z klas potomnych

Wykorzystuje klasę TempConverter do konwersji między systemami Celsjusza i Fahrenheita. Wysyła sygnały gdy temperatura się zmienia

Program zawiera instancje następujących klas: Instancja TempConverter Dwa widgety QGroupBox, a każdy z nich zawiera: Widget QDial Widget QLCDNumber

class TempConverter : public QObject QObject jako rodzic { Q_OBJECT Makro Q_OBJECT Wskaźnik do rodzica public: TempConverter(int tempcelsius, QObject *parent = 0); int tempcelsius() const; int tempfahrenheit() const; public slots: void settempcelsius(int); void settempfahrenheit(int); signals: void tempcelsiuschanged(int); void tempfahrenheitchanged(int); private: int m_tempcelsius; }; Akcesory i modyfikatory Emitowane gdy zmienia się temperatura Wewnętrzna reprezentacji temperatury w Celsjuszach

Slot settempcelsius: void TempConverter::setTempCelsius(int tempcelsius) { if(m_tempcelsius == tempcelsius) Stop rekurencji return; m_tempcelsius = tempcelsius; Zmień stan obiektu } emit tempcelsiuschanged(m_tempcelsius); emit tempfahrenheitchanged(tempfahrenheit()); Slot settempfahrenheit: Emisja sygnałów o zmianie void TempConverter::setTempFahrenheit(int tempfahrenheit) { int tempcelsius = (5.0/9.0)*(tempFahrenheit-32); settempcelsius(tempcelsius); } Konwersja na Celsjusze

valuechanged settempcelsius tempcelsiuschanged setvalue Pokrętłami zarządza obiekt klasy TempConverter Zmiany na wyświetlaczach LCD są warunkowane przez TempConverter zmiany pokręteł settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged valuechanged display tempfahrenheitchanged setvalue valuechanged settempfahrenheit connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

valuechanged settempcelsius tempcelsiuschanged setvalue Użytkownik przesuwa pokrętło zmiany temperatury w Celsjuszach TempConverter settempcelsius settempfahrenheit tempcelsiuschanged tempfahrenheitchanged tempfahrenheitchanged setvalue valuechanged settempfahrenheit valuechanged display connect(celsiusdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempCelsius(int))); connect(celsiusdial, SIGNAL(valueChanged(int)), celsiuslcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempCelsiusChanged(int)), celsiusdial, SLOT(setValue(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), tempconverter, SLOT(setTempFahrenheit(int))); connect(fahrenheitdial, SIGNAL(valueChanged(int)), fahrenheitlcd, SLOT(display(int))); connect(tempconverter, SIGNAL(tempFahrenheitChanged(int)), fahrenheitdial, SLOT(setValue(int)));

Często istnieje potrzeba przekazania wartości w instrukcji connect connect(key, SIGNAL(clicked()), this, SLOT(keyPressed(1))); Przykładowo implementując klawiaturę: Nie jest to prawidłowy zapis

Rozwiązanie #1: wiele slotów {... connections public slots: void key1pressed(); void key2pressed(); void key3pressed(); void key4pressed(); void key5pressed(); void key6pressed(); void key7pressed(); void key8pressed(); void key9pressed(); void key0pressed(); }...

Rozwiązanie #2: własna implementacja przycisku { QPushButton QIntPushButton... signals: void clicked(int); { QIntPushButton *b; b=new QIntPushButton(1); connect(b, SIGNAL(clicked(int)), this, SLOT(keyPressed(int))); b=new QIntPushButton(2); connect(b, SIGNAL(clicked(int)), this, SLOT(keyPressed(int))); b=new QIntPushButton(3); connect(b, SIGNAL(clicked(int)), this, SLOT(keyPressed(int))); }... }...

#1: wiele slotów Wiele slotów zawierających niemalże ten sam kod Kod trudny w utrzymaniu Trudne rozszerzenie funkcjonalności (ciągle nowy slot) #2: własna implementacja przycisku Nowa wyspecjalizowana klasa Trudne rozszerzenie funkcjonalności

Klasa QSignalMapper rozwiązuje ten problem Przyporządkuje wartość do każdego emitera Jest warstwą pośredniczącą { QSignalMapper *m = new QSignalMapper(this); QPushButton *b; b=new QPushButton("1"); connect(b, SIGNAL(clicked()), m, SLOT(map())); m->setmapping(b, 1);... Przyporządkuj emiterowi wartość Stwórz QSignalMapper Połącz przyciski do warstwy pośredniczącej Połącz pośrednika do slotu } connect(m, SIGNAL(mapped(int)), this, SLOT(keyPressed(int)));

Każdy przycisk ma przyporządkowaną wartość liczbową {... connections QSignalMapper connection public slots: void keypressed(); }... Warstwa pośrednicząca (QSignalMaper) emituje sygnał mapped(int), który przekazuje przyporządkowaną danemu przyciskowi wartość.