České vysoké učení technické v Praze Fakulta stavební Semestrální projekt letní semestr 2009 Softwarový seminář 1 (YSS1) Katedra mapování a kartografie Grafické rozhraní pro GNU Sqltutor Autoři: Zora Hořejšová, Adam Polívka, Kateřina Šmídová
Obsah 1 Zadání 3 2 Úvod 3 2.1 Instalace.......................................... 3 3 Projekt 4 3.1 Práce v týmu....................................... 4 3.2 Source code management................................. 4 4 Struktura aplikace 5 4.1 Dvojjazyčnost....................................... 5 5 Datové struktury a algoritmy 6 5.1 Grafická část....................................... 6 5.1.1 Class AnswerResultWidget............................ 6 5.1.2 Class MainWindow................................ 7 5.1.3 Class OptionsDialog............................... 8 5.1.4 Class ResultsForm................................ 9 5.1.5 Class TableViewForm............................... 10 5.1.6 Class TableWidget................................ 10 5.2 Negrafická část...................................... 11 5.2.1 Class Options................................... 11 5.2.2 Class Permutation................................ 12 5.2.3 Class Question.................................. 12 6 Závěr 13 A UML diagramy 14
1 Zadání Navrhněte a naprogramujte grafické rozhraní pro aplikaci GNU Sqltutor pomocí knihovny Qt 4.5. 2 Úvod Webová aplikace GNU Sqltutor je výukový program SQL (strukturovaný dotazovací jazyk) od Prof. Ing. Aleše Čepka, CSc., používaný při samostudiu a záverečném testování studentů předmětu Databázové systémy na Stavební fakultě ČVUT v Praze. Vzhledem k tomu, že je volně přístupný na adrese http://sqltutor.fsv.cvut.cz/cgi-bin/sqltutor, může ho využívat kdokoliv i mimo uvedený předmět a vysokou školu. Úkolem projektu SQLTutor je tedy vytvořit možnost pro uživatele spouštět si aplikaci offline bez internetového prohlížeče, tedy bez nutnosti nainstalovat server Apache. Přetrvává požadavek nainstalované databáze a její naplnění daty, ale i to by se mohlo v budoucnu změnit, kdy by se mohla používat databáze na serveru josef.fsv.cvut.cz (tedy databáze používaná webovou verzí - GNU Sqltutor), čímž by opět vznikla onlineová aplikace. 2.1 Instalace Vlastní aplikace nevyžaduje instalaci a rovnou se spouští. Co je ale potřeba nainstalovat je databázová část a následně ji naplnit daty. Průběh instalace bude popsán pro linuxové distribuce používající balíčkovací systém APT. Instalace databázového systému PostgreSQL: apt-get install postgresql postgresql-client Vytvoření databáze a potřebných uživatelů: su su - postgres createdb sqltutor createuser -D -R -S sqlquiz psql -c "ALTER USER sqlquiz WITH PASSWORD sqlkrok ;" createuser -d -r -S sqlexec psql -c "ALTER USER sqlexec WITH PASSWORD sqlkrok ;" psql sqltutor -c "CREATE LANGUAGE plpgsql;" Pro plnění databáze je potřeba vytvořit uživatele s příslušnými právy, který má dostatečná práva i v operačním systému. Např.:
createuser -r -d -s root Vlastní data se získají s aplikací GNU Sqltutor: apt-get install cvs cvs -d:pserver:anonymous@cvs.sv.gnu.org:/sources/sqltutor co sqltutor Naplnění databáze: cd sqltutor/./configure cd database/ make install cd../datasets/ make install cd../tutorials/ make install Tento postup naplní databázi aniž by se musel kompilovat cgi-script, avšak aby úspěšně doběhl configure, musí být nainstalované knihovny pro kompilaci. Pokud se provede trojice kroků./configure, make, make install, databáze se naplní také, ale kvůli kompilaci to potrvá o chvilku déle. 3 Projekt 3.1 Práce v týmu Na projektu se pracovalo ve tříčleném týmu pod vedením Zory Hořejšové (pravila skromně). Vzhledem k faktu, že prakticky stejná aplikace již existovala, tudíž bylo předem jasné co bude naše aplikace umět a jak bude přibližně vypadat, nebylo nutné pořádat žádné meetings a veškerá komunikace probíhala pře ICQ nebo e-mail. Práce nebyla nijak předem rozdělena, a každý dělal co bylo potřeba. Při psaní kódu se ale práce oddělovala a každý pracoval na samostané části aplikace, a o svou část se danný člen týmu staral až do ukončení vývoje. 3.2 Source code management Vývoj aplikace v týmu se neobejde bez verzovacího systému, který značně usnadňuje spojování částí kódu od jednotlivých vývojářů a jejich aktualizaci. Zároveň tento systém slouží jako zálohování dat. Z několika možných verzovacích systémů jsme zvolili Subversion, jelikož jsme s jeho používáním již měli určité zkušenosti. Pro hosting našeho projektu jsme zvolili produkt společnosti Sun Microsystems - Kenai (http://kenai.com). Stránky projektu jsou ke shlédnutí na adrese http://kenai.com/projects/sqltutor/, kde je také zobrazeno url pro checkout repositoře, nebo je zde možné stáhnout pouze aplikaci samotnou. K nahlédnutí je zde také anglický manuál k aplikaci: http://sqltutor.kenai.com.
4 Struktura aplikace Jak už bylo napsáno výše, přibližná struktura aplikace byla známa předem díky existující v základě stejné aplikace GNU Sqltutor. Přesto jsme spíše pro zajímavost vytvořili dva UML diagramy, přiložené na konci dokumentu. Aplikace se zkládá ze třech základních částí, které odpovídají třem oknům. Jsou to OptionsDialog - úvodní okno s nastavením testu, MainWindow - okno zobrazující testovací otázky a ResultsForm - okno zobrazující výsledky testu. Mimo zobrazování obsahu se každé okno stará i o databázovou část aplikace, but přímo voláním SQL dotazů nebo nepřímo voláním metod jiných objektů. Mezi tyto akce patří například otevírání a zavírání session, ukládání informací o zodpovězené otázce či načítání další otázku. Zmíněné objekty jsou instancemi pouze pár tříd, jako je Options - sdílená všemi okny, Permutation, která byla převzata z původní cgi-aplikace GNU Sqltutor, nebo Question. Některé objekty jako jsou OptionsDialog a MainWindow se vytvářejí již ve funkci main, jelikož mezi sebou musí komunikovat. Veškerá komunikace je obstarána pomocí signálů a slotů a vlastně se omezuje pouze na otevírání konkrétních oken: //Ukonceni aplikace instanci tridy OptionsDialog QObject::connect(optionsDialog, SIGNAL(quitApplication()), qapplication, SLOT(quit())); //Ukonceni aplikace instanci tridy MainWindow QObject::connect(mainWindow, SIGNAL(quitApplication()), qapplication, SLOT(quit())); //Zobrazeni MainWindow z objektu OptionsDialog QObject::connect(optionsDialog, SIGNAL(showMainWindow()), mainwindow, SLOT(showAndSetUp())); //Zobrazeni OptionsDialog z objektu MainWindow QObject::connect(mainWindow, SIGNAL(showOptionsDialog()), optionsdialog, SLOT(show())); Ve funkci main se také otevírají spojení s databází, pokud neúspěšně, aplikace se po oznámení chyby uživateli ihned ukončí. 4.1 Dvojjazyčnost Původní aplikace komunikovala s uživatelem pomocí angličtiny, ale díky velkému nadšení Adama Polívky byla přeložena také do češtiny a uživateli je nyní nabídnut výběr ze dvou jazyků. Překládání qt aplikace je velice jednoduché díky programu Qt Linguist, který nahrazuje editaci XML souborů. Aby bylo možné měnit jazyk, je nutné umístit všechny řetězce(u kterých je změna jazyku požadována) do funkce tr(). Do souboru nazev projektu.pro se přidají požadované názvy jazykových XML souborů:
TRANSLATIONS = languages/sqltutorgui_cs.ts \ languages/sqltutorgui_en.ts Aby se tyto soubory vytvořili, spustí se příkaz lupdate nazev_projektu.pro Vytvořené soubory je možno editovat přímo, jelikož jde o obyčejné XML. Pohodlnější ale je použít sw Qt Linguist. Po dokončení překladu je ještě nutné.ts soubory zkompilovat but přímo v programu Linguist funkcí Release nebo příkazem lrelease v příkazové řádce. V kódu se pak změna jazyka ošetřuje následovně: QTranslator *translator = new QTranslator(); //zjisteni jazyka podle systemoveho locale QString lang = QLocale().system().name().section("_",0,0); //nastaveni jazyka if(translator->load("languages/sqltutorgui_" + lang)){ qapplication->installtranslator(translator); } 5 Datové struktury a algoritmy Aplikace nebyla nijak algoritmicky náročná, tudíž zde budou zmíněny pouze třídy. Kompletní zrdojové kódy s projektem pro Qt Creator jsou ke stažení na stránkách projektu http://kenai.com/projects/sqltutor/downloads/download/sqltutor_src.zip, nebo je možný checkout repositoře kde ale nemusí být stabilní verze. 5.1 Grafická část 5.1.1 Class AnswerResultWidget https://kenai.com/svn/sqltutor~svn, Jedná se o widget obashující informace o testové otázce a odpovědi uživatele. Používá se v ResultsForm v zobrazení seznamu otázek. class AnswerResultWidget : public QWidget { Q_OBJECT AnswerResultWidget(QSqlDatabase *db, QWidget *parent = 0); ~AnswerResultWidget(); void showandsetup(options *opt, QSqlRecord res); protected: void changeevent(qevent *e);
Ui::AnswerResultWidget *m_ui; QSqlDatabase *database; 5.1.2 Class MainWindow Tato třída je hlavním oknem aplikace, ve kterém se odehrává vlastní testování. Většina jejích metod obsluhuje události vyvolané uživatelem a zobrazuje mu příslušná data. class MainWindow : public QMainWindow { Q_OBJECT MainWindow(Options *opt, QSqlDatabase *db, QSqlDatabase *dbexec, QWidget *parent = 0); ~MainWindow(); //centruje widget do stredu plochy void centerposition(qwidget *widget); void newtest(); //otevre web browser s napovedou static void showmanual(); protected: void closeevent(qcloseevent *event); void changeevent(qevent *e); Ui::MainWindowClass *ui; //spojeni s databazi, pouze pro cteni QSqlDatabase *database; //spojeni s databazi, pro zapis QSqlDatabase *databaseexec; //nastaveni aktualniho testu Options *options; //okno pro zobrazovani db tabulek TableViewForm tableviewform; //okno pro zobrazeni vysledku ResultsForm resultsform; //poradove cislo aktualni otazky int numberquestion; //pocet spravne zodpovezenych otazek int correctanswers; //prubezny ziskany pocet bodu int points;
//zda byly jiz body pripocteny do promenne points (na jednu otazku se totiz muze zodpov bool wasincrementedresult; //retezec obsahujici informace o aktualnim nastaveni QString optionsstring; //aktualni otazka Question question; void nextquestion(); void finishtest(); void setupstatusbar(); public slots: //zobrazi okno a nastavi widgety do zakladniho stavu (naplni je datama...) void showandsetup(); private slots: void on_manualpushbutton_clicked(); void on_finishpushbutton_clicked(); void on_executepushbutton_clicked(); void on_helppushbutton_clicked(); void on_nextquestionpushbutton_clicked(); void on_datasettablewidget_doubleclicked(const QModelIndex & index); signals: void quitapplication(); void showoptionsdialog(); 5.1.3 Class OptionsDialog Dialog zobrazující se vždy na začátku každého testu. Slouží k nastavení následujícího testu a zároveň uživateli nabízí změnu jazyka. Při odsouhlasení tohoto dialogu se otevírá nová session pomocí volání metody setoptions objektu Options. class OptionsDialog : public QDialog { Q_OBJECT OptionsDialog(QApplication *a, QSqlDatabase *db, Options *opt, QWidget *parent = 0); ~OptionsDialog(); protected: void changeevent(qevent *e); Ui::OptionsDialog *m_ui; void setup(); void filltutorialcombobox(qsqldatabase *db); void filldatasetcombobox(qsqldatabase *db);
void fillpointslineedits(qsqldatabase *db); void filllanguagecombobox(); // nastavi jazyk podle systemu void setlanguagebylocale(); Options *options; QSqlDatabase *database; TableViewForm tableviewform; QApplication *application; QTranslator *translator; QString currentlanguage; private slots: void on_manualpushbutton_clicked(); void on_quitbutton_clicked(); void on_okbutton_clicked(); void on_datasetspushbutton_clicked(); void on_tutorialcombobox_currentindexchanged(int index); void on_languagecombobox_currentindexchanged(int index); void on_minpointsspinbox_valuechanged(int min); void on_maxpointsspinbox_valuechanged(int max); signals: void quitapplication(); void showmainwindow(); void showlanguagedialog(); 5.1.4 Class ResultsForm Okno zobrazující celkový výsledek proběhnutého testu, spolu s nastavením a seznamem všech použitých otázek s odpověd mi. Voláním metody closesession třídy Options se zavírá otevřená session. Uživatel zde má možnost přejít na OptionsDialog a tím spustit nový test. class ResultsForm : public QWidget { Q_OBJECT ResultsForm(QSqlDatabase *db, QMainWindow *mw, QWidget *parent = 0); ~ResultsForm(); void showandsetup(options *opt); protected: void changeevent(qevent *e); void closeevent(qcloseevent *event);
Ui::ResultsForm *m_ui; QSqlDatabase *database; Options *options; QMainWindow *mainwindow; void fillresults(qsqldatabase *db); void fillanswers(qsqldatabase *db); private slots: void on_newtestpushbutton_clicked(); 5.1.5 Class TableViewForm Jednoduchý widget obsahující pouze tabulku ve formě QTableView. Okno slouží ke zobrazení určité tabulky, která je mu předávána v podobě instance třídy QSqlQueryModel. class TableViewForm : public QWidget { Q_OBJECT TableViewForm(QWidget *parent = 0); ~TableViewForm(); void filltable(qsqldatabase &db, QString select); protected: void changeevent(qevent *e); Ui::TableViewForm *m_ui; QSqlQueryModel *model; 5.1.6 Class TableWidget Pomocný widget sloužící k zobrazení seznamu tabulek pro dannou otázku. class TableWidget : public QTableWidget { Q_OBJECT TableWidget(QWidget *parent = 0); ~TableWidget(){} //vyplni se seznamem tabulek a vrati zneni otazky QString fill(qsqldatabase *db, int tutorialid, int questionid );
5.2 Negrafická část 5.2.1 Class Options Třída se používá jako úložiště aktuálního nastavení. Zároveň má metody pro otevírání a zavírání aktuální session. class Options { Options(QSqlDatabase *db): database(db){set = false;} Options(int minmin, int maxmax); //setters bool setoptions(int tutorial, int min, int max, bool help, QString dataset); //nastavi povolene hodnoty min a max promennych void setminmax(int min, int max){minminpoints = min; maxmaxpoints = max; set = false;} //getters bool isset(){return set;} int gettutorial(){return tutorial;} int getminpoints(){return minpoints;} int getmaxpoints(){return maxpoints;} bool ishelp(){return help;} QString getdataset(){return dataset;} int getsession(){return session;} QString gethash(){return hash;} //zavre aktualni session (v db) void closesession(); //promenne nastaveni int tutorial; int minpoints; int maxpoints; bool help; QString dataset; //pomocne promenne bool set; int minminpoints; int maxmaxpoints; QSqlDatabase *database;
//otevre novou session (v db) bool opensession(); //id aktualni session int session; QString hash; 5.2.2 Class Permutation Tato třída je převzata z původní aplikace GNU Sqltutor a používá se při kontrole správnosti odpovědi. Třída vytváří všechny možné kombinace pořadí sloupců výsledné tabulky pro dannou otázku. Tyto kombinace se poté ve třídě Question porovnávají se správným výsledkem dokud se nenalezne shoda nebo se nevyčerpají všechny kombinace. class Permutation { Permutation() : N(0),avail(0),total(0){ Permutation(int k){reset(k); void next(); void reset(int size); int size() const {return N; int perms() const {return total; bool empty() const {return avail == 0; int& operator[](int i){return perm[i]; int N; QVector<int> perm; int avail; int total; 5.2.3 Class Question Třída uchovávající informace o aktuální otázce. Kontroluje správnost odpovědi a odpověd ukládá do databáze. class Question { Question(Options *opt, QSqlDatabase *db, QSqlDatabase *dbexec); //Vrati-li plpgsql funkce nextquestion dosud nezadanou otazku odpovidajici nastaveni, //nastavi promenne nove otazky a vrati true. Neexistuje-li takova otazka, vrati false. bool next();
//zkontroluje odpoved a vrati vysledek kontroly bool checkanswer(const QString& useranswer); int getid(){return questionid; QString getquestiontext(){return questiontext; QString getdataset(){return dataset; int getpoints(){return points; QString gettutoranswer(){return tutoranswer; QString getmessage(){return message; Options* options; QSqlDatabase* database; QSqlDatabase* databaseexec; // ulozi odpoved do databaze, nastavi zpravu o vysledku kontroly a vrati hodnotu correc bool saveanswer(const bool& correct,const QString& answer,const QString& msg); // pomocna fce na odstraneni stredniku z konce dotazu QString semicolumn(const QString & query); //zprava o vysledku kontroly spravnosti QString message; int tutorialid; int questionid; QString dataset; // bodove ohodnoceni otazky int points; // text otazky QString questiontext; // spravna odpoved QString tutoranswer; 6 Závěr Výsledkem toho projektu je jednoduchá Qt aplikace, která je grafickým rozhraním databázové části webové aplikace GNU Sqltutor. Při programování aplikace jsme se seznámili hlavně s SQL modulem knihovny Qt. Grafickou část knihovny jsme samozřejmě využívali ve velkém množství, ale díky grafickému designeru, který je nedílnou součástí Qt Creatoru, jde spíše o klikání než o psaní kódu, což není myšleno jako výtka. Další důležitou částí Qt knihovny jsou signály a sloty. Jejich výhodu jsme našli hlavně ve faktu, že se objekty navzájem vůbec nemusejí znát a přesto spolu můžou komunikovat. Vzhledem k nevelké obsáhlosti projektu jsme byli nuceni využít pouze velice malou část Qt knihovny, tudíž jsme porozuměli pouze zlomku. Naučili jsme se ale způsob práce s knihovnou, a tak by pronikání do dalších modulů nemělo být složité.
A UML diagramy Usecase Diagram
Sequence Diagram