====== Návrhové vzory - factory ======= [[factory]] Factory má za úkol vytvářet instance objektů. \\ Ukazatel na třídu //Factory// slouží jako //"proměnná uchovávající konstruktor třídy"// ===== Abstrakní třída Factory ===== Třída **Factory** bude obsahovat jen metodu **createInstance** Výsledný typy metody //createInstance// bude třída společná pro vytvářené objekty, v našem případě **QWidget**. Metoda bude abstraktní * **= 0** * nebude mít implementaci * nelze vytvářet exempláře přímo z třídy //Factory// pomocí //new Factory// class Factory { public: virtual QWidget * createInstance () = 0; }; ===== Factory pro jednotlivé grafické prvky ===== Vytvoříme třídu **ButtonFactory** odvozenou z třídy //Factory//. \ Implemetujeme metodu //createInstance//. class ButtonFactory : public Factory { public: QWidget * createInstance () { return new QPushButton ("Button"); } }; Odvozená třída může skladovat další data. \\ Např. **EditorFactory** skladuje **text**, který byl parametrem konstruktoru. \\ A //createInstance// použije //text// při vytváření instance. class EditorFactory : public Factory { private: QString text; public: QWidget * createInstance () { QTextEdit * e = new QTextEdit; e->setText (text); return e; } EditorFactory (QString p_text) : text (p_text) { } }; class TreeFactory : public Factory { public: QWidget * createInstance () { return new QTreeWidget; } }; ===== Použití Factory v jednoduché aplikaci ===== Jednotivé //Factory// budeme skladovat v //asociativním poli// **factories**. \\ Indexem bude jméno předávané pomocí //drag and drop//, jméno bude typu //QString//. \\ Hodnoty v poli budou ukazatele na jednotlivé //Factory//. \\ Samotné pole bude typu **QHash**, což je obdoba **std::map**. extern QHash factories; Během //drag and drop// budeme data typu //Factory// přenášet jako data našeho následujícího formátu: const QString tool_format = "application/x-shape"; ===== Tlačítka pro jednotlivé "Factory" ===== Třídu **ToolButton** odvodíme ze třídy **QToolButton**, bude obsahovat: * proměnná **name** - jméno předávané pomocí //drag and drop// a v našem případě i nadpis tlačítka * virtuální funkci **mousePressEvent** původně deklarovanou v nadřazené tříde **QWidget** reagující na stisk tlačítka pomocí myši * konstruktor který uloží svůj parametr do proměnné //name// a současně zobrazí text na tlačítku class ToolButton : public QToolButton { private: QString name; protected : void mousePressEvent (QMouseEvent *event); public: ToolButton (QWidget * p_parent, QString p_name) : QToolButton (p_parent), name (p_name) { setText (p_name); } }; Metoda //mousePressEvent// zahájí //"tažení dat pomocí myši"//. * vytvoříme objekt typu **QDrag** objsahují parametry pro //drag and drop//, v konstruktoru předáme jako parametr naše okno * přenášená data budou uložena v objektu typu **QMimeData**, stejný typ se používá i pro práci se schránkou (//clipboard//) * data mohou být uložena vetool_format více formátech (např. text, obrázek, ...) z nichž si cílová aplikace může vybrat pro sebe vhodný formát * použijeme náš formát //"application/x-shape"// a předáme jméno //Factory// jako //ascii// řetězec znaků * data uložíme do objekt typu //QDrag// * nakonec zahájíme //drag and drop// void ToolButton::mousePressEvent (QMouseEvent *event) { QDrag * drag = new QDrag (this); QMimeData * data = new QMimeData (); data->setData (tool_format, name.toLatin1()); drag->setMimeData (data); drag->start (); } ===== Cílový panel pro jednotlivé grafické prvky ===== Plochu hlavního okna naší aplikace bude pokrývat třída **Panel** odvozená od **QWidget**. \\ class Panel : public QWidget { Q_OBJECT public: explicit Panel (QWidget *parent = nullptr); protected: void dragEnterEvent (QDragEnterEvent *event); void dropEvent (QDropEvent *event); }; Konstruktor bude mít za parametr odkaz na nadřazené okno, \\ tento odkaz předáme konstruktoru na nadřazenou třídu **QWidget** \\ a povolíme vhazování dat pomocí myši **setAcceptDrops (true)** Panel::Panel(QWidget *parent) : QWidget (parent) { setAcceptDrops (true); } ==== Odsouhlasení nebo odmítnutí tažených dat ==== Virtuální funkce **dragEnterEvent** pocházející z nadřazené třídy //QWidget// \\ vyzvedne odkaz na přenášená data do proměnné typu ** const QMimeData * ** \\ a pokud data obsahují náš formát, tak //drop// odsouhlasí. Výsledek se projeví na vzhledu kurzoru myši během taření dat. void Panel::dragEnterEvent (QDragEnterEvent *event) { const QMimeData * data = event->mimeData (); if (data->hasFormat (tool_format)) { event->accept(); } } ==== Vhození dat ==== Implementujeme virtální metodu **dropEvent** * opět vyzvedneme tažená data * zkontrolujeme formát, ale jelikož jsem odsouhlasili jen náš formát, je to zbytečné * do proměnné **name** uložíme data prřenášená v rámci našeho formátu * z pole //factories// vyzvedneme objekt představující naší **factory** * pomocí //drag and drop// přenášíme text, nikoliv objekt typy //Factory// * **použití factory** : zavoláme **factory->createInstance ()** a tím vytvoříme grafický prvek * do proměnné **pos** uložíme souřadnici myši při vhození dat * grafický prvek přidáme do panelu * posuneme na zmíněnou souřadnici * grafický prvek zobrazíme factory->createInstance () void Panel::dropEvent (QDropEvent *event) { const QMimeData * data = event->mimeData (); if (data->hasFormat (tool_format)) { QString name = data->data(tool_format); Factory * factory = factories [name]; QWidget * widget = factory->createInstance (); QPoint pos = event->pos(); widget->setParent (this); widget->move (pos); widget->setVisible(true); } } ==== Přidání virtuálních funkcí z nadřazených tříd v prostředí Qt Creator ==== Pravou myší na identifikátor //Panel// nebo //QWidget//, **Refactor**, **Insert Virtual Functions of Base Classes** {{factory_virtual.png}} ===== Hlavní okno s paletou nástrojů ===== class MainWindow : public QMainWindow { public: MainWindow(QWidget *parent = nullptr); void addTool (QString text, Factory * factory); /* ... */ }; Funkce **addTool** * přidá //factory// do pole **factories** * vytvoří tlačítko s příslušným textem * přidá tlačítko do nástrojové lišty void MainWindow::addTool (QString name, Factory * factory) { factories [name] = factory; ToolButton * b = new ToolButton (this, name); ui->toolBar->addWidget (b); } V konstruktoru vytvoříme jednotlivé nástroje umístěné do palety, funkci //addTool// předáme jako parametry * jméno zobrazované na tlačítku a předávané během //drag and drop// * nový exemplář **Factory**, který bude uložen v poli //factories// a použit při vhození dat pomocí myši MainWindow::MainWindow(QWidget *parent) { /* ... */ addTool ("Button", new ButtonFactory); addTool ("Edit", new EditorFactory ("some text")); addTool ("Tree View", new TreeFactory); } Vzhled okna po vhození jednoho exempláře od každé //"factory"// {{factory.png}} [[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/tree/master/Factory|gitlab]]