C++ Qt6 Создание чатов в мессенджере
Всем добрый день, форумчане?
Стоит задача сделать сделать чаты в корпоративном мессенджере. Не понимаю как лучше за это взяться средствами Qt, чтобы потом не попасть в тупик.
В чатах должны поддерживаться
- текстовые сообщения
- голосовые
- изображения
- документы, контакты и т.д.
Библиотеку для связи с сервером я уже проверил и подключил в проект.
Пока такие идеи для окна самого чата:
- QListWidget – можно запихнуть виджеты, но это тяжеловесно
- QListView – наш вариант, но проблема со стилизацией
- QScrollArea и класть виджеты в layout.
Но если использовать QListWidget то я не смогу делать fetch из локальной БД, ведь мне надо кэшировать сообщения - к примеру держать в памяти не более 100 элементов списка.
QListView хороший вариант, но ведь сами элементы списка (блок сообщения, блок голосового и т.д.) мне надо будет рисовать через QPainter в методе paint(), а есть требование использовать qss стили из файла...
Для QScrollArea непонятно как отмерять номера текущих строк. В общем прописывать заново логику списка.
Ответы (1 шт):
Самый лучший вариант это связка QListView + QStyledItemDelegate + [кастомный виджет/ы для отображения элементов чата].
Под кастомным виджетом я понимаю что есть виджет-вопрос, есть виджет-ответ, каждый из которых может иметь собственный набор функций и внешний вид.
Здесь я приведу простой пример того, как использовать простой кастомный виджет, передавать в него строку, отрисовывать его в наследном классе QStyledItemDelegate и загружать в него стили qss из файла.
Допустим у нас где-то создается и инициализируется QListView таким образом:
// Вертикальный список
ui->listView->setFlow(QListView::TopToBottom);
// Здесь используется стандартная модель со строками
// Но, безусловно, можно использовать какую угодно модель данных
ui->listView->setModel(
new QStringListModel({"Item 1", "Item 2", "Item 3", ...})
);
// Загружаем свой делегат отображения данных
ui->listView->setItemDelegate(new MyItemDelegate());
MyItemDelegate.hpp:
class MyItemDelegate: public QStyledItemDelegate {
public:
// Отрисовка нашего кастомного элемента чата
void paint(
QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
// Размер строки чата
QSize sizeHint(
const QStyleOptionViewItem &option, const QModelIndex &index) const;
}
MyItemDelegate.cpp:
void MyItemDelegate::paint(
QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
// Строка сообщения
const QString& value = index.data().toString();
// Виджет элемента чата
ChatItemWidget chatItem;
// инициализируем его текстом
chatItem.setText(value);
// Задаем размеры
chatItem.setGeometry(option.rect);
// Теперь надо этот виджет отрисовать в ячейке списка
// Задаем размер буфера рендера виджета на холсте
QPixmap pixmap(chatItem.size());
// Рендерим виджет в буфер
chatItem.render(&pixmap);
// Отрисовываем буфер на холсте
painter->drawPixmap(option.rect.topLeft(), pixmap);
}
// Метод возвращает рекомендуемый размер строки списка
QSize MyItemDelegate::sizeHint(
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QSize(option.rect.width(), 80);
}
Из данного примера видно, что конкретная реализация ChatItemWidget может быть какой угодно, состоящей из различных графических элементов/виджетов, наша реализация делегата отрисует его таким, какой он есть.
В моем примере ChatItemWidget выглядит так:
// hpp
class ChatItemWidget : public QWidget
{
Q_OBJECT
public:
explicit ChatItemWidget (QWidget *parent = nullptr);
~ChatItemWidget ();
void setText(const QString& value);
private:
Ui::Item *ui;
};
// cpp
ChatItemWidget::ChatItemWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Item)
{
ui->setupUi(this);
// Загружаем файл со стилями
QFile styleFile("Путь/к/файлу/со/стилями/style.qss");
if (styleFile.open(QFile::ReadOnly | QFile::Text)) {
QString styleSheet = styleFile.readAll();
this->setStyleSheet(styleSheet);
styleFile.close();
}
}
void ChatItemWidget::setText(const QString &value)
{
ui->label->setText(value);
}
Содержимое файла style.qss выглядит так:
QLabel {
border: 2px solid green;
border-radius: 4px;
padding: 2px;
}
Теперь я немного поменял дизайн ChatItemWidget на такой:
Чуть изменил qss:
QLabel {
border: 2px solid green;
border-radius: 10px;
padding: 2px;
background-color: white;
}
Больше ничего не трогал и получил следующий результат:


