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);
}

gitlab

 
uop/visitor.txt · Last modified: 2020/12/14 21:18 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki