Podstawy Qt4 I, artykuły
[ Pobierz całość w formacie PDF ]
Podstawy biblioteki Qt4. Część I
Radosław 'revcorey' Marek
Introdukcja
Poniższy tekst stanowi pierwszą część z serii trzech artykułów wprowadzających w
bibliotekę Qt4 obecnie należącą do firmy nokia. Zostanie omówione tylko i wyłącznie absolutne
minimum wymagane aby tworzyć proste aplikacje wykorzystujące GUI i proste mechanizmy
biblioteki, ważne jest aby przed lekturą czytelnik był zaznajomiony z językiem C++ i znał takie
pojęcie jak wielodziedziczenie. Oczywiście z powodów objętościowych zawre tylko najważniejsze
informację i skupię tylko na tych jakie mogą być potrzebne dla osób studiujących na naszym
wydziale.
Skrzynka narzędziowa
Będziemy korzystać z
Qt SDK: Complete Development Environment
na licencji LGPL. W tym
że sdk oprócz biblioteki zawarte jest także środowisko IDE Qt creator z którego obędziemy
korzystać. Jeśli jednak ktoś woli inne IDE w takim wypadku na stronach nokii znajduje się wtyczka
dla IDE eclipse a także dla ms visual studio. W sdk znajdują się także przykłady aplikacji
napisanych w qt4. Użytkownicy systemów operacyjnych linuks mogą pobrać bibliotekę i narzędzia
z repozytoriów dystrybucji o ile to możliwe. SDK i instrukcje instalacji znajdują się na oficjalnej
stronie biblioteki. Wszelkie pliki programów znajdują się w dziale download strimera w folderze
artykuły w odpowiednio oznaczonych folderach.
Pierwszy Program
Pierwszy program ma na celu ukazanie jak zbudować okno,dodać przyciski,pole w którym można
wpisywać tekst.
mainwindow.h
class mainwindow : public QMainWindow
{
Q_OBJECT
public:
mainwindow();
~mainwindow(){}
public slots: //1
void ShowDial();
void AddText();
private:
QWidget * glowny_widget;
QTextEdit * text;
QPushButton * add_text;
QPushButton * show_dial;
};
Klasa QMainWindow po której dziedziczy nasza klasa mainwindow zapewnia główne okno dla
aplikacji. To właśnie w nim dodamy widget w tym przypadku obiekt o nazwie glowny_widget w
którym następnie umieścimy interesujące nas elementy. Ale za nim przejdziemy dalej musimy zająć
się jednym z fundamentów biblioteki tzn. mechanizmem sygnałów i slotów.
Sloty i Sygnały
Slotami w zasadzie są to po prostu funkcje w klasie które możemy wywoływać normalnie tak samo
jak w zwykłym C++ . Dopiero zastosowanie ich w połączeniu z sygnałami pokazuje siłę tych
funkcji. W pliku mainwindow.h jak widzimy zadeklarowałem public slots(punkt 1 w komentarzu)
równie dobrze mógłbym dać private aby tylko ta klasa mogła z nich korzystać. Oznacza to że w tym
miejscu deklaruje funkcje które są slotami ale oczywiście mogę je nadal wywołać je normalnie jak
każdą inną funkcję w klasie. Można by powiedzieć że na razie nic nie osiągnęliśmy bo gdzie są
osławione sygnały. Tu musimy zajrzeć do implementacji konstruktora
mainwindow.cpp
mainwindow::mainwindow()
{
glowny_widget=new QWidget();
setCentralWidget(glowny_widget);
text=new QTextEdit();
add_text=new QPushButton(tr("Add Text"));
show_dial= new QPushButton(tr("Show Dial"));
connect(add_text,SIGNAL(clicked()),this,SLOT(AddText())); // pkt.2
connect(show_dial,SIGNAL(clicked()),this,SLOT(ShowDial()));
QVBoxLayout *mainLayoutt = new QVBoxLayout();
mainLayoutt->addWidget(text);
mainLayoutt->addWidget(add_text);
mainLayoutt->addWidget(show_dial);
glowny_widget->setLayout(mainLayoutt);
}
void mainwindow::ShowDial(){
QMessageBox::information(this, tr("jupi"),
tr("click ok"));
}
void mainwindow::AddText(){
text->clear();
text->setText("Simple Text");
}
W punkcie 2 widzimy połączenie sygnału ze slotem. Oczywiście nasuwa się pytanie gdzie jest
implementacja tego sygnału? Odpowiedź znajdujemy już w samej definicji funkcji connect
mianowicie:
connect(nadawca,SIGNAL(sygnał()),odbiorca,SLOT(slot()))
W przypadku pkt. 2 nadawcą jest obiekt add_text który to jest przyciskiem(QPushButton) kiedy na
niego klikniemy przycisk ten emituje sygnał clicked() ponieważ połączyliśmy sygnał clicked() tego
przycisku ze slotem AddText() zostanie on wywołana właśnie ta funkcja i wpisany w obiekt
QTextEdit tekst Simple Text . Już pre definiowane obiekty takie jak QPushButton,QSlidet itd.
posiadają wbudowane sygnały i sloty. Oczywiście my także w naszych własnych klasach możemy
tworzyć oprócz slotów sygnały. Np.
w naszej klasie(u nas przykładowa klasa pomarancza w pliku pomarancza.h) w której chcemy
zaimplementować sygnał wpisujemy
/plik pomarancza.h
//jesteśmy w klasie pomarancza
//tworzymy własny slot i sygnał tym razem,nie polegamy na wbudowanych
public slots:
void setval(int val);
signals:
void change(int value);
private:
int myval
//w pliku pomarancza.cpp
void pomarancza::setval(int val){
myval = val;
emit change(val);}
następnie tworzymy gdzieś egzemplarze klasy pomarancza
pomarancza a,b;
connect(&a,SIGNAL(change(int)),&b,SLOT(setval(int)));
a.setval(10);//takie wywołanie ustawi w obiekcie a wartość równą 10 ale także w obiekcie b
//ponieważ zostanie wyemitowany sygnał z wartością 10 a zostanie przechwycony przez slot
//setval w obiekcie b
Co ciekaw sygnał może być połączony z innym sygnałem
connect(obiekt1, SIGNAL(//odpowiedni sygnał),
this, SIGNAL(//odpowiedni sygnał));
Sygnały i sloty można rozłączać
disconnect(obiekt1, SIGNAL(//odpowiedni sygnał),
obiekt2, SLOT(//odpowiedni sygnał));
Na koniec musimy pamiętać jeśli korzystamy ze slotów i sygnałów w naszej klasie MUSI
znajdować się makro Q_OBJECT
Rozkład Widgetów
Podczas umieszczania widgetów na formie nie interesuje nas ich przypadkowe rozmieszczenie a
raczej już z góry ustalone. Dlatego wróćmy do konstruktora
mainwindow.cpp
mainwindow::mainwindow()
{
glowny_widget=new QWidget();
setCentralWidget(widget);
text=new QTextEdit();
add_text=new QPushButton(tr("Add Text"));
show_dial= new QPushButton(tr("Show Dial"));
connect(add_text,SIGNAL(clicked()),this,SLOT(AddText()));
connect(show_dial,SIGNAL(clicked()),this,SLOT(ShowDial()));
QVBoxLayout *mainLayoutt = new QVBoxLayout(); //pkt. 3
mainLayoutt->addWidget(text); //pkt.4
mainLayoutt->addWidget(add_text);
mainLayoutt->addWidget(show_dial);
glowny_widget->setLayout(mainLayoutt); //pkt.5
}
biblioteka Qt udostępnia różne layouty m.in możemy ułożyć Widgety jeden pod drugim albo obok
siebie w poziomie itp. Pełna galeria layoutów znajduje się w dokumentacji. My w pkt. 3 tworzymy
nowy QVBoxLayout i pkt. 4 umieszczamy widget. A w pkt.5 ustawiamy dla naszego głównego
widgetu ten typ rozmieszczenia. Nie co skomplikowane bo nie powiedziałem o hierarchii widgetów.
W naszym programie dziedziczymy QMainWindow co zapewnia główne okno dla programu, obiekt
widget(klasa QWidget) ustawiamy jako centralny obiekt naszej klasy mainwidnow(która
odziedziczyła po QMainWindow) teraz obiekt o nazwie widget stał się potomkiem dla mainwindow
podobnie będzie kiedy nasze przyciski umieścimy w obiekcie widget i to one będą potomne
względem obiektu o nazwie widget.
Wracając do layotów jest możliwe zagnieżdżanie ich tzn. w jednym layoucie umieszczamy
poziomo dwa przyciski obok siebie, kolejno tworzymy następny layout który sprawia że elementy
są układane pionowo następnie dodajemy do nowo stworzonego layout , uprzedni layout z dwoma
przyciskami poziomo obok siebie i po raz kolejny dodajemy dwa przyciski ale te przyciski zostanÄ…
ustawione pionowo pod poprzednim layoutem. krótki kod zakładamy że layout z poziomymi
przyciskami(o nazwie poziomo) stworzono już,dodano przyciski a także nowy layout o nazwie
pionowo także jest już stworzony a dodawanie wygląda tak:
pionowo->addLayout(poziomo);
pionowo->addWidget(nowy_przycisk1);
pionowo->addWidget(nowy_przycisk2);
glowny_widget->setlayout(pionowo);
Szybkie Tworzenie GUI
Jak łatwo zauważyć powyższy sposób tworzenia GUI może być dość pracochłonny po to
powstał qtdesigner. W skrócie praca z qtdesignerem(obecnie zintegrowany z qtcreatorem) wygląda
tak iż przerzucamy interesujące nas widgety na formę, następnie zapisujemy ten plik z tak
stworzonego pliku z końcówką .ui generowany jest plik .h . W tym miejscu także poznamy
podstawy zapisu i odczytu danych z dysku.
W qtcreator mamy zazwyczaj domyślnie tworzony plik .ui podczas tworzenia nowego
projektu po wybraniu opcji stworzenia aplikacji z GUI w opcjach wyboru projektu. Przechodzimy
do katalogu Forms w projekcie. Wybieramy nasz plik .ui (oczywiście nowe pliki .ui możemy
dodawać do projektu).
Teraz w klikamy dwukrotnie na menubar(w lewym górnym rogu) i nazywamy go file. Teraz
klikamy na niego raz i wchodzimy w niego dodajemy dwie pozycje open i save.
Na główną formę wrzucamy QTextEdit z menu po lewej stronie ,po kliknięciu prawym przyciskiem
myszy(ppm) na niego i wybranie opcji zmiany nazwy obiektu zmieniamy wpisujemy nasz_tekst.
Kolejno klikamy ppm na pustym miejscu na formie wybieramy Lay out i Lay out Vertical.
Oczywiście gdyby było więcej elementów istnieje możliwość przerzucenia dodatkowych layoutów
na formÄ™ podobnie jak QTextEdit. Przechodzimy teraz do mainwindow.h
mainwindow.h
#include <QMainWindow>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
bool save();
bool open();
protected:
void changeEvent(QEvent *e);
private:
Ui::MainWindow *ui;
};
W zasadzie najbardziej interesujące są sloty(zwracać mogą void, typ bool jest tylko przypadkiem)
save i open które zostały dodane ręcznie reszta klasy została wygenerowana automatycznie. Jak
widać część pracy wykonało już IDE. na samym końcu mamy obiekt ui to właśnie on reprezentuje
naszÄ… formÄ™.
Przechodzimy do pliku mainwindow.cpp(tu przytoczę fragmenty ponieważ plik jest obszerniejszy)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);//1
connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(open()));//2
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(save()));
}
setupUI() ustawia parametry początkowe naszego widgetu musi być zawsze na początku jeśli tu
korzystamy z stworzonej formy w qtdesigner.
W pkt. 1 Å‚Ä…czymy obiekty QAction(actionOpen,actionSave) z odpowiednimi slotami.
bool MainWindow::save(){
//Zapisanie wersja binarna
QString fileName = QFileDialog::getSaveFileName(this,
tr("Save"), "",
tr("(*.txt);;All Files (*)")); //1
if(fileName.isEmpty()){//2
return false;}
else {
QFile file(fileName);//3
if (!file.open(QIODevice::WriteOnly)) {//4
QMessageBox::information(this, tr("Unable to save file"),
file.errorString());
return false;
}
QDataStream out(&file);//5
out.setVersion(QDataStream::Qt_4_6);//6
out << ui->nasz_tekst->toPlainText();//7
return true;
/* /////////////////////////////////////////Wersja 2/////////////////////////////////////////////////////////////////////////
Zapisanie wersja Tekstowa, jest ona nie co skrócona tzn. skróciłem blok if a także plik jest z
miejsca zapisywany pod określoną nazwą aby było krócej
QFile file("test2.txt"); //można użyć QFileDialog z poprzedniego przykłądu aby wybrać nazwę
if (file.open(QFile::WriteOnly)) {
QTextStream out(&file);//1
out << ui->nasz_tekst->toPlainText();//2
}
} */
}
[ Pobierz całość w formacie PDF ]