QT: строим дерево строк на основе QTreeWidget
Примеров для новичков на работу с деревьями почти нет, а хочется, для начала, что-нибудь элементарное, скажем, чтобы мы могли добавлять в произвольное дерево и удалять из него строковые элементы. Ввод строк пользователем или их чтение из файла, заботу об "украшении" приложения и тому подобные типовые вещи опустим, а сосредоточимся лишь на главном.
Создадим проект-потомок QWidget
, компоненты интерфейса разместим при помощи "волшебных комбинаций клавиш" Ctrl+H и Ctrl+L:
- в режиме дизайна добавим на форму 2 кнопки, выделим обе при зажатой клавише Ctrl, нажмём комбинацию Ctrl+H, получили горизонтальное "обрамление"
QHBoxLayout
; - выше кнопок добавим на форму компоненту
QTreeWidget
, выделим её и красную рамку вокруг кнопок при зажатой клавише Ctrl, нажмём комбинацию Ctrl+L, получили вертикальныйQVBoxLayout
; - щёлкнули по форме вне всех компонент, нажали Ctrl+H - растянули компоненты на всю форму.
На рисунке видно, что должно выйти, а также подписи на кнопках.
форма приложения QTree
Выбирать элементы дерева мы будем кликом по ним, так что нажмём правую кнопку мыши на пустом treeWidget
и создадим единственный слот (команды Перейти к слоту - itemClicked(QTreeWidgetItem *, int)
).
А для кнопок аналогично выберем самый простой слот clicked()
и создадим ещё 2 пустые функции.
В приватной секции файла widget.h
допишем имена своих методов и свойств, которые мы добавляем в класс, вот что получится:
private: Ui::Widget *ui; //ниже - наши данные int treeCount(QTreeWidget *, QTreeWidgetItem *); //подсчёт количества элементов в QTreeWidget void DeleteItem (QTreeWidgetItem *currentItem); //удаление элемента из QTreeWidget void InsertItem (QTreeWidgetItem *, QString); //добавление элемента в QTreeWidget void showAll(void); //вывод информации о QTreeWidget int count; //переменная для хранения номера очередного узла QTreeWidgetItem *currentItem; //текущий элемент, запоминается при клике в QTreeWidget int currentColumn; //номер столбца, на самом деле будет = 0, т.к. у нас 1 столбец
Не забудем также подключить библиотеку QtWidgets:
#include <QWidget> #include <QtWidgets>
Теперь напишем код файла widget.cpp
. В конструктор добавим инициализацию наших данных:
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //ниже наша часть конструктора count = 0; ui->treeWidget->setColumnCount(1); QStringList headers; headers << tr("Объекты"); ui->treeWidget->setHeaderLabels(headers); currentItem = NULL; currentColumn = 0; }
Метод подсчёта количества узлов в QTreeWidget
нам придётся сделать довольно громоздким, так как готового свойства нет и не может быть (нужно с видом использовать модель, и тогда всё будет, но для начинающих сразу начать это делать сложновато):
int Widget::treeCount(QTreeWidget *tree, QTreeWidgetItem *parent = 0) { /* не учтёт свёрнутые ветви; потому что правильно было бы делать через модель */ tree->expandAll(); //а это "костыль" int count = 0; if (parent == 0) { int topCount = tree->topLevelItemCount(); for (int i = 0; i < topCount; i++) { QTreeWidgetItem *item = tree->topLevelItem(i); if (item->isExpanded()) { count += treeCount(tree, item); } } count += topCount; } else { int childCount = parent->childCount(); for (int i = 0; i < childCount; i++) { QTreeWidgetItem *item = parent->child(i); if (item->isExpanded()) { count += treeCount(tree, item); } } count += childCount; } return count; }
Удаление элемента учтёт случаи, когда элемент имеет родителя и когда является корневым:
void Widget::DeleteItem (QTreeWidgetItem *currentItem) { QTreeWidgetItem *parent = currentItem->parent(); int index; if (parent) { index = parent->indexOfChild(ui->treeWidget->currentItem()); delete parent->takeChild(index); } else { index = ui->treeWidget->indexOfTopLevelItem(ui->treeWidget->currentItem()); delete ui->treeWidget->takeTopLevelItem(index); } }
Вставка будет предполагать, что родитель есть, иначе придётся писать 2 разных метода вставки с различными параметрами:
void Widget::InsertItem (QTreeWidgetItem *parent, QString text) { if (parent->isExpanded()==false) parent->setExpanded(true); QTreeWidgetItem *newItem = new QTreeWidgetItem(parent, ui->treeWidget->currentItem()); newItem->setText (currentColumn, text); newItem->setExpanded(true); }
По кнопке "Добавить" будет выполняться следующее:
void Widget::on_pushButton_clicked() { //кнопка Добавить if (currentItem) { QString word = currentItem->text(currentColumn); InsertItem (currentItem, word + " " + QString("%1").arg(++count)); } else { QTreeWidgetItem *newItem = new QTreeWidgetItem(ui->treeWidget, ui->treeWidget->currentItem()); //указываем 2-м параметром текущий элемент как предшествующий newItem->setText (currentColumn, "" + QString("%1").arg(++count)); newItem->setExpanded(true); } currentItem = NULL; showAll(); }
Как видно, здесь мы учли, что можно добавлять элементы в корень дерева (так в нашей программе будет всегда, если перед добавлением не щёлкнуть по элементу, который станет родителем нового элемента).
Кнопка "Удалить", по сути, только вызовет написанный выше метод:
void Widget::on_pushButton_2_clicked() { //кнопка Удалить if (currentItem) { DeleteItem (currentItem); currentItem = NULL; } showAll(); }
Если никакой элемент не выделен, то удалять она тоже ничего не будет.
Служебная функция showAll
позаботится об отображении вычисленного количества узлов в заголовке окна виджета.
void Widget::showAll(void) { int cnt = treeCount (ui->treeWidget); QString str(tr("Всего: ")+QString("%1").arg(cnt)); setWindowTitle(str); }
Ну и наш единственный слот "дерева" просто будет запоминать в дополнительных свойствах класса, по какому именно элементу мы щёлкнули:
void Widget::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column) { currentItem = item; currentColumn = column; }
Это всё, проект можно запускать и выйдет вот что:
окно виджета QTree, на рисунке выделен элемент
Почему наше решение вышло довольно неуклюжим? А потому что правильно было бы использовать MVC. Но об этом в другой раз, возможно, на таком же простом "списочном" или "древовидном" примере :)
Скачать архив .zip с папкой этого проекта QT5 (3 Кб)
06.04.2016, 23:39 [30372 просмотра]