В этой серии статей описан процесс разработки приложения для рисования графов на языке C++ с помощью Qt. Результат можно посмотреть на видео.
Предыдущие статьи:
2. Подключение примера elasticnodes
3. Разбор GraphWidget, и реализация добавлении вершин
4. Выделение и удаление вершин
Реализуем кнопку Соединение вершин
Соединить вершины можно двумя способами. В первом случае на сцене ничего не выделено. Тогда для соединения необходимо нажать на кнопку "стрелка" и отметить вершину-источник, затем вершину-назначение. Во втором на сцене уже отмечена вершина. И после нажатия на кнопку "стрелка" останется выбрать вершину назначения.
Добавление поля классу Node
Для наглядности, в процессе соединения будем отмечать вершину-источник, окружностью розового цвета. Для этого добавим приватное поле _mark, которое будет использоваться как флаг отметки вершины. Добавим в список инициализации _mark(0).
Используя подсказки, добавим методы установки и получения значения.
В функции отрисовки Paint будем выбирать цвет ручки в зависимости от значения флага:
painter->setPen(QPen(_mark ? Qt::magenta : Qt::black, 2));
Дополнительное поле в классе Widget
В классе Widget добавляем поле отвечающее за процессом соединения (int connProcess). Будет 3 состояния:
1. Когда ничего ни с кем соединять не надо.
2. Когда необходимо отметить вершину-источник.
3. Когда необходимо отметить вершину-назначение.
Создадим перечисление, для наглядности в файле widget.h:
namespace CONN {
enum {
NONE,
NEED_SOURCE,
NEED_DEST
};
}
В списке инициализации connProcess(CONN::NONE).
Реализация функции кнопки "стрелка"
При нажатии на кнопку Соединить, будем проверять количество выделенных элементов. Если нет выделенных элементов, значит connProcess = CONN::NEED_SOURCE. Если один выделенный элемент, и выделенный элемент является Node, отмечаем ( setMark(true)) выделенную вершину и меняем состояние connProcess = CONN::NEED_DEST. Запустим и проверим, как окружность меняет цвет при нажатии на кнопку "стрелка". Код показан ниже.
Для того, чтобы это исправить, необходимо добавить в очередь на перерисовку этот элемент, вызвав update() в методе setMark().
Обработка события изменения выделенных элементов сцены
При выделении элементов (так при потере выделения) вызывается у сцены сигнал selectionChanged. Использовал эту функцию, но сейчас переделал на сигнал MouseClick. Эту функцию можно увидеть в коммитах.
Обработка нажатия на GraphWidget
У GraphWidget нет сигнала нажатия. Поэтому сделаем его сами, перезагрузив событие
void mousePressEvent(QMouseEvent *event).
И реализация mousePressEvent:
void GraphWidget::mousePressEvent(QMouseEvent *event)
{
QGraphicsView::mousePressEvent(event);
emit mousePressedSignal(event);
}
В классе Widget создадим слот для привязки с mousePressedSignal. Реализуем его.
Функция itemAt(const QPoint & pos) возвращает верхний элемент в точке pos , либо nullptr.
Запустим программу и посмотрим, как она работает:
А теперь попробуем удалить вершину.
Ребра вершины также исчезли со сцены. Если попробуем переместить один из вершин, который был связан с удаленной вершиной, то наша программа вылетит.
Посмотрев стек вызовов, видим, что вершина пытается "отрегулировать"(adjust) уже удаленное ребро(Edge).
Пришло время заняться классом Edge. Для исправления достаточно при удалении ребра (в деструкторе), удалять его из списка ребр в связных вершинах. У нас есть метод для добавления ребр (addEdge). Аналогично делаем для удаления.
Метод removeEdge.
void Node::removeEdge(Edge *edge)
{
edgeList.removeOne(edge);
}
Деструктор тоже простой:
На 10-ой строчке заменим 0 на nullptr
И вот наша программа уже строит вершины и может соединять их ребрами. Дальше сделаем красивые стрелки и добавим возможность выделения и удаления стрелок.
Продолжение следует.
Проект выложен на github.com/fryn3/widGraph.
Если прочитал до этого места, то, вероятно, материал был интересен. Поддержи лайком.
Также если появились вопросы - пиши мне в телегу или вк.