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