====== Visitor ====== V našem příkladě návrhový vzor //visitor// "navštíví" všechny uzly stromu typu //TreeItem// a zavolá metodu //visit//. ===== Třída TreeItem představující zpracovávané uzly stromu ===== Třída **TreeItem** je rozšířením standardní třídy **QTreeWidgetItem** o proměnnou obsahující cestu k danému souboru nebo adresáři. class TreeItem : public QTreeWidgetItem { public: QString path; }; ===== Třída Visitor ===== Třída **Visitor** obsahuje * veřejně dostupnou metodu **process**, které jako parametr zadáme strom **QTreeWidget**, jehož uzly chceme zpracovat * privátní metodu **processBranch**, která rekurzivně zpracuje jednu větev stromu * protected metodu **visit**, kterou zavoláme pro jednotlivé uzly představující soubory * metoda je abstraktní ( **= 0** ), z naší třídy //Visitor// musíme odvodit konkrétní třídu, která metodu //visit// implementuje class Visitor { protected: virtual void visit (QFileInfo & info) = 0; private: void processBranch (QTreeWidgetItem * branch); public: void process (QTreeWidget * tree); }; Metoda **process** využije //formální kořenový prvek stromu// a zavolá //processBranch// void Visitor::process (QTreeWidget * tree) { processBranch (tree->invisibleRootItem ()); } Metod **processBranch** se pokusí převédst obecný uzel stromu na náš typ //TreeItem//. \\ A pokud se to povede, vyzvedne cestu k souboru a vytvoří **info** typu **QFileInfo**. \\ Pokud //info// představuje soubor (a nikoliv adresář), zavolá metodu //visit//. Nakonec rekurzivnš zavoláme //processBranch// pro vnořené větve stromu. void Visitor::processBranch (QTreeWidgetItem * branch) { TreeItem * item = dynamic_cast < TreeItem * > (branch); if (item != nullptr) { QFileInfo info (item->path); if (info.isFile()) visit (info); } int cnt = branch->childCount(); for (int i = 0; i < cnt; i++) { processBranch (branch->child (i)); } } ===== Použití třídy Visitor ===== Třída **FileSizeVisitor** odvozená ze třídy //Visitor//, \\ navštíví uzly stromu představující soubory, \\ vytiskne jejich jména, \\ spočítá soubory a jejich celkovou délku, Třída obsahuje proměnné používané při zpracování stromu * proměnná **cnt** bude obsahovat počet souborů * v proměnné **sum** bude celkový počet bytů * **output** je ukazatel na okénko pro výstup textu class FileSizeVisitor : public Visitor { private: int cnt; long long sum; QTextEdit * output; void print (QString s); protected: void visit (QFileInfo & info); public: FileSizeVisitor (QTreeWidget * tree, QTextEdit * edit); }; Konstruktor : * vhodně inicializuje proměnné * zavolá metodu **process** z původní třídy **Visitor** * vytiskne výsledky FileSizeVisitor::FileSizeVisitor (QTreeWidget * tree, QTextEdit * edit) { output = edit; cnt = 0; sum = 0; process (tree); print (QString::number (cnt) + " files, " + fmt (sum) + " bytes"); } Metoda **proces** pro jednolivé souboty zavolá metodu **visit**. void FileSizeVisitor::visit (QFileInfo &info) { print ("file " + info.fileName()); cnt ++; sum = sum + info.size(); } Pomocná funkce **print** přidá jednu řádku do výstupního okénka void FileSizeVisitor::print (QString s) { output->append (s); } ===== Hlavní okno ===== Zavoláme **FileSizeVisitor**, parametry bude strom a výstupní okénko.\\ ( Ani nedeklarujeme proměnnou typu //FileSizeVisitor//, jen zavoláme konstruktor. ) FileSizeVisitor (ui->treeWidget, ui->textEdit); Ještě musíme naprogramovat metodu **showDir**, která naplní strom soubory a adresáři. class MainWindow : public QMainWindow { /* ... */ void showDir (QTreeWidgetItem * target, QString path); }; Metoda **showDir** * pro zadanou cestu **path** vytvoří objekt **dir** typu **QDir** * //QDir// nám cestu, která může být např. jen tečka nebo dvě tečky, převede na úplnou cestu **absolutePath** se všemi adresáři * krátké jméno jen s posledním adresářem získéme pomocí **dirName** * **dir.entryInfoList** nám poskytne seznam souborů a podadresářů v daném adresáři * filtr **NoDotAndDotDot** vyloučí formální adresáře **.** a **..** * druhý parametr způsobí setřídění podle jména souborů * pro podadresáře rekurzivně zavoláme //showDir// * pro soubory vytvoříme uzly ve stromu void MainWindow::showDir (QTreeWidgetItem * target, QString path) { QDir dir (path); TreeItem * branch = new TreeItem; branch->path = dir.absolutePath (); branch->setText (0, dir.dirName()); branch->setToolTip (0, branch->path); branch->setForeground(0, QColor ("red")); branch->setIcon(0, QIcon::fromTheme ("folder")); target->addChild (branch); QDir::Filters filter = QDir::AllEntries | QDir::NoDotAndDotDot; QDir::SortFlags sort = QDir::Name; for (QFileInfo info : dir.entryInfoList (filter, sort)) { if (info.isDir()) { showDir (branch, info.absoluteFilePath()); } else { TreeItem * node = new TreeItem; node->path = info.absoluteFilePath(); node->setText (0, info.fileName()); node->setToolTip (0, node->path); node->setForeground (0, QColor ("blue")); node->setIcon (0, QIcon::fromTheme ("document")); branch->addChild (node); } } } MainWindow::MainWindow (QWidget *parent) { /* ... */ QDir dir = QDir (".."); showDir (ui->treeWidget->invisibleRootItem(), dir.canonicalPath()); FileSizeVisitor (ui->treeWidget, ui->textEdit); } {{visitor.png}} [[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/tree/master/Visitor|gitlab]]