Table of Contents
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); }