#include <QMimeData> #include <QDrag> #include <QMouseEvent> class ToolButton : public QToolButton { public: ToolButton(); private: QString name; QIcon icon; protected: void mousePressEvent (QMouseEvent *event); public: ToolButton (QString p_name, QString p_icon_name = ""); }; void addToolButtons (QToolBar * page);
Zavedeme vlastní formát dat application/x-tool.
Formát s tímto názvem bude přenášet řetězec znaků s jménem tlačítka (rectangle, ellipse, line)
const QString toolFormat = "application/x-tool"; void ToolButton::mousePressEvent (QMouseEvent * event) { if (event->button() == Qt::LeftButton ) { QMimeData * mimeData = new QMimeData; mimeData->setData (toolFormat, name.toLatin1()); QDrag * drag = new QDrag (this); drag->setMimeData (mimeData); drag->setPixmap (icon.pixmap (24, 24)); drag->setHotSpot (QPoint (-16, -16)); Qt::DropAction dropAction = drag->exec (Qt::MoveAction | Qt::CopyAction | Qt::LinkAction); } }
Konstruktor má za parametry jméno tlačítka a cestu k souboru s obrázkem.
ToolButton::ToolButton (QString p_name, QString p_icon_name) : name (p_name) { if (p_icon_name == "") p_icon_name = ":/icons/" + p_name + ".svg"; icon = QIcon (p_icon_name); setIcon (icon); setToolTip (name); }
Pokud souboru s obrázkem není zadán pokusíme se najít soubor podle jména tlačítka mezi “zakompilovanými” soubory.
Soubory začínající dvojtečkou jsem již použili pro soubor s počátečními daty. Čtení XML souboru
“Zakompilované” soubory jsou popsány v soubory resources.qrc.
Jsou tam tři ikonky pro naše nástroje.
<RCC> <qresource prefix="/"> <file>icons/ellipse.svg</file> <file>icons/line.svg</file> <file>icons/rectangle.svg</file> <file>data/abc.xml</file> </qresource> </RCC>
Soubor scene.cc
Metoda dragEnterEvent schvaluje druh dat tažených myší.
Totéž musíme udělat i při pohybu myši nad scénou.
void Scene::dragEnterEvent (QGraphicsSceneDragDropEvent * event) { const QMimeData * mimeData = event->mimeData(); if (mimeData->hasColor() || mimeData->hasFormat (toolFormat)) { event->setAccepted (true); } else { event->setAccepted (false); } } // see http://stackoverflow.com/questions/4177720/accepting-drops-on-a-qgraphicsscene void Scene::dragMoveEvent (QGraphicsSceneDragDropEvent *event) { const QMimeData * mimeData = event->mimeData(); if (mimeData->hasColor() || mimeData->hasFormat (toolFormat)) { event->setAccepted (true); } else { event->setAccepted (false); } }
Pokud “upustíme” data na scénu, vyzvedneme jméno nástroje a souřadnici vzhledem k rohu scény.
Funkce itemFromTool podle zadaného jména vytvoří grafický prvek.
Ten přidáme do scény nebo do jiného prvku na tomto místě.
void Scene::dropEvent (QGraphicsSceneDragDropEvent * event) { const QMimeData * mimeData = event->mimeData(); if (mimeData->hasColor()) { /* ... */ } if (mimeData->hasFormat (toolFormat)) { QString tool = mimeData->data (toolFormat); QPointF p = event->scenePos (); int x = int (p.x ()); int y = int (p.y ()); QGraphicsItem * item = itemFromTool (tool, x, y); QGraphicsItem * target = itemAt (p, QTransform ()); if (target == nullptr) addItem (item); else item->setParentItem (target); win->refreshTree (); } }
io.c
QGraphicsItem * itemFromTool (QString tool, int x, int y) { QGraphicsItem * result = nullptr; if (tool == "rectangle") { QGraphicsRectItem * item = new QGraphicsRectItem; item->setRect (0, 0, 100, 80); item->setPen (QColor ("blue")); item->setBrush (QColor ("yellow")); item->setToolTip ("rectangle"); item->setFlags (QGraphicsItem::ItemIsMovable | QGraphicsEllipseItem::ItemIsSelectable); result = item; } else if (tool == "ellipse") { QGraphicsEllipseItem * item = new QGraphicsEllipseItem; item->setRect (0, 0, 60, 40); item->setPen (QColor ("orange")); item->setBrush (QColor ("lime")); item->setToolTip ("ellipse"); result = item; } else if (tool == "line") { QGraphicsLineItem * item = new QGraphicsLineItem; item->setLine (0, 0, 60, 40); item->setPen (QColor ("red")); item->setToolTip ("line"); result = item; } if (result != nullptr) { result->setPos (x, y); result->setFlags (QGraphicsItem::ItemIsMovable | QGraphicsEllipseItem::ItemIsSelectable); } return result; }
Pokud nástroj myší táhneme nad stromu (místo nad scénu), musíme předefinovat několik virtuálních funkcí stromu.
Soubor tree.h
class Tree : public QTreeWidget { protected: virtual QStringList mimeTypes () const override; // virtual QMimeData * mimeData (const QList<QTreeWidgetItem *> items) const override; virtual bool dropMimeData (QTreeWidgetItem * parent, int index, const QMimeData * data, Qt::DropAction action) override; virtual Qt::DropActions supportedDropActions () const override; };
Soubor tree.cc
Funkce mimeTypes vrací seznam přijatelných formátů dat. (Namísto dragEnterEvent.)
Obdobně metoda supportedDropActions povoluje přesun, kopírování nebo vyvtváření odkazu.
QStringList Tree::mimeTypes() const // <-- importatnt { return QStringList () << "application/x-color" << toolFormat; } Qt::DropActions Tree::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction; }
Pokud vhozená data obsahují barvu, uložíme ji do proměnné color.
Pokud byla provváděno kopírování (bylo stisklé ctrl) obarvíme okraj grafického prvku.
V opačném případě obarvíme vnitřek.
Pokud vhozená data obsahují jméno nástroje, vytvoříme nový grafický prvek, prvek přidáme do scény a informaci o něm do stromu.
bool Tree::dropMimeData (QTreeWidgetItem * target, int index, const QMimeData * data, Qt::DropAction action) { bool result = false; TreeNode * node = dynamic_cast < TreeNode * > (target); if (data->hasColor ()) { QColor color = data->colorData().value <QColor> (); if (node != nullptr) { QAbstractGraphicsShapeItem * shape = dynamic_cast < QAbstractGraphicsShapeItem * > (node->item); if (shape != nullptr) { if (action == Qt::CopyAction) /* ctrl */ shape->setPen (color); // need #include <QPen> else shape->setBrush (color); } } result = true; } else if (data->hasFormat (toolFormat)) { QString tool = data->data (toolFormat); QGraphicsItem * item = itemFromTool (tool, 0, 0); if (item != nullptr) { if (node == nullptr) scene->addItem (item); else item->setParentItem (node->item); } win->refreshNewTreeItem (item); } return result; }