Каждый разработчик сталкивался с этим: интерфейс вроде бы работает, но пользователи путаются, кликают «не туда» и совершают ошибки. Выбор есть — но он реализован так, что становится источником проблем, а не удобства.
Эта глава раскроет, почему элементы выбора — один из самых коварных участков GUI. Вы обнаружите, как простые списки, таблицы и вкладки могут либо ускорить работу пользователя, либо незаметно разрушить UX и производительность приложения.
Будет показано, чем на практике отличаются QListWidget, QTreeWidget и QTableWidget, когда режим пиктограмм превращается в ловушку, и почему добавление виджетов внутрь элементов может замедлить приложение в разы. Также затрагиваются сигналы, сортировка, drag & drop и тонкости редактирования — ровно столько, чтобы увидеть систему целиком.
Пропустить эту главу — значит позже расплачиваться за архитектурные решения, принятые «на глаз».
Доступ к архиву с полностью готовыми примерами для компиляции и 16 бесплатными главами книги — идеальный способ сразу закрепить материал на практике.
Самопроверка по главе
Почему использование виджетов в элементах списка (QListWidget, QTreeWidget) существенно снижает быстродействие?Ответ
Правильный ответ: Каждый виджет требует собственной отрисовки, обработки событий и управления ресурсами. При сотнях элементов это создает серьезную нагрузку, поэтому рекомендуется использовать представления (например, checkState для флажков) вместо реальных виджетов.
В чем ключевое преимущество архитектуры «модель-представление» над классами QListWidget, QTreeWidget и QTableWidget?Ответ
Правильный ответ: Архитектура «модель-представление» разделяет данные и их отображение, что дает большую гибкость, возможность использовать одни данные в нескольких представлениях и лучшую производительность при работе с большими объемами данных.
Какая комбинация флагов нужна элементу списка, чтобы пользователь мог его выбрать, редактировать и перетаскивать?Ответ
Правильный ответ: Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled. Каждый флаг отвечает за конкретную возможность взаимодействия.
Почему для сортировки элементов по датам или числовым значениям недостаточно просто вызвать метод sortItems()?Ответ
Правильный ответ: По умолчанию sortItems() использует алфавитную (лексикографическую) сортировку строк. Для правильной сортировки по датам или числам нужно унаследовать класс элемента и перезаписать оператор operator<(), чтобы сравнивать значения корректно.
В каких ситуациях QComboBox предпочтительнее QListWidget, даже если функционально они похожи?Ответ
Правильный ответ: Когда важна экономия места в интерфейсе и пользователю нужно выбрать только один элемент из списка. QComboBox показывает только текущий выбор, раскрываясь лишь на момент выбора.
Зачем использовать представление кнопки флажка через Qt::ItemIsUserCheckable вместо добавления реального виджета QCheckBox?Ответ
Правильный ответ: Это значительно улучшает производительность списка, особенно при большом количестве элементов (сотни и более). Представление флажка не создает реальный виджет, а лишь отрисовывает его внешний вид.
В чем разница между сигналами activated() и currentIndexChanged() в QComboBox?Ответ
Правильный ответ: activated() отправляется при каждом выборе, даже если пользователь выбрал тот же элемент повторно. currentIndexChanged() отправляется только при реальном изменении текущего элемента.
Что произойдет, если вызвать sortItems(), а затем добавить новые элементы в список методом addItem()?Ответ
Правильный ответ: Новые элементы не будут автоматически отсортированы и добавятся в конец списка. Потребуется повторный вызов sortItems() для сортировки всех элементов.
Как установить режим множественного выделения для QListWidget или QTreeWidget?Ответ
Правильный ответ: Вызвать метод setSelectionMode() с параметром QAbstractItemView::MultiSelection. Для запрета выделения используется NoSelection, а для выбора только одного элемента — SingleSelection.
Какой метод позволяет получить список всех выбранных элементов в QListWidget, если включен режим множественного выделения?Ответ
Правильный ответ: Метод selectedItems() возвращает список всех выбранных элементов. Для получения одного текущего элемента используется currentItem().
Зачем в QTreeWidget используется QTreeWidgetItemIterator, и какой флаг передать для обхода только выделенных элементов?Ответ
Правильный ответ: Итератор позволяет легко обойти все элементы дерева, включая вложенные. Для обхода только выделенных элементов нужно передать флаг QTreeWidgetItemIterator::Selected в конструктор итератора.
Для чего предназначен класс QTabWidget, и в каких ситуациях его использование наиболее оправдано?Ответ
Правильный ответ: QTabWidget разбивает сложное диалоговое окно с большим количеством опций на серию логически скомпонованных простых окон, делая интерфейс более понятным и удобным для пользователя.
Как проверить, установлен ли флажок в элементе иерархического списка с представлением кнопки флажка?Ответ
Правильный ответ: Нужно вызвать метод checkState() с номером столбца и сравнить результат с Qt::Checked: if (ptwi->checkState(0) == Qt::Checked).
Практические задания
Простой уровень
Справочник с категориями
Создайте приложение с выпадающим списком (QComboBox), содержащим категории: “Фрукты”, “Овощи”, “Ягоды”. При выборе категории в простом списке (QListWidget) должны отображаться соответствующие элементы. Например, для “Фрукты”: яблоко, банан, апельсин. Добавьте иконки к элементам списка.
Подсказки: Используйте сигнал currentIndexChanged() от QComboBox для отслеживания выбора категории. При смене категории очищайте список методом clear() и заполняйте новыми элементами. Для иконок используйте QListWidgetItem::setIcon(). Храните данные в QMap<QString, QStringList> для удобства.
Средний уровень
Менеджер задач с приоритетами
Разработайте менеджер задач на основе QTableWidget с тремя столбцами: “Задача”, “Приоритет”, “Статус”. Пользователь должен иметь возможность добавлять новые задачи через QLineEdit, выбирать приоритет из QComboBox (низкий/средний/высокий) и отмечать выполнение через флажок в ячейке. Реализуйте сортировку по приоритету при клике на заголовок столбца. Добавьте кнопку удаления выбранной строки.
Подсказки: Используйте setSortingEnabled(true) для таблицы. Для флажков применяйте setCheckState() к QTableWidgetItem. Для удаления строки используйте removeRow() с индексом из currentRow(). Для кастомной сортировки по приоритету создайте наследника QTableWidgetItem с перегруженным operator<().
Сложный уровень
Файловый навигатор с drag & drop
Создайте двухпанельный файловый навигатор с использованием QTreeWidget. Левая панель показывает иерархию папок (минимум 3 уровня вложенности), правая — содержимое выбранной папки в режиме пиктограмм (QListWidget с IconMode). Реализуйте перетаскивание файлов между панелями с визуальной индикацией. При двойном клике на папке в правой панели она должна открываться в левой. Добавьте контекстное меню с опциями “Создать папку”, “Переименовать”, “Удалить”. Используйте QTabWidget для организации нескольких открытых папок.
Подсказки: Включите drag & drop через setDragEnabled(true) и setAcceptDrops(true). Обработайте dropEvent() для реакции на перетаскивание. Используйте сигнал itemClicked() для синхронизации панелей. Для контекстного меню создайте QMenu и покажите его в обработчике contextMenuEvent(). Храните путь к папке в QTreeWidgetItem через setData(). Для вкладок используйте addTab() с уникальными экземплярами панелей.
💬 Присоединяйтесь к обсуждению!
Разобрались с элементами выбора? Появились вопросы о том, когда использовать QListWidget, а когда лучше перейти к архитектуре «модель-представление»?
Делитесь своими решениями практических заданий, обсуждайте тонкости оптимизации производительности списков и помогайте другим читателям освоить материал главы!
Ваш опыт и вопросы помогают всему сообществу лучше понимать Qt!