====== 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]]