Russian Qt Forum

Qt => Вопросы новичков => Тема начата: tamplier от Май 09, 2020, 18:19



Название: html-форматирование в QTableView
Отправлено: tamplier от Май 09, 2020, 18:19
Уважаемые форумчане!
Скажите, пожалуйста, каким образом можно "прикрутить" отображение строк с html-форматированием в QTableView?

Если упростить, то ситуация следующая (просто не знаю даже куда копать): есть список строк QStringList. Есть модель QStringListModel, которая является моделью данного списка и есть представление QTableView, которое отображает список строк (модели) с одним столбцом. Необходимо чтобы определенные буквы в представлении (в строках) отображались жирным шрифтом (либо, как-то выделялись)... Понятно, что в контексте QStringList и QStringListModel данную задачу решить не получится.
Были мысли использовать QList<QLabel *> с QAbstractListModel. Но не могу понять как это всё реализовать...
Еще как вариант использовать делегат, но с темой делегатов я на Вы, почитав книги М.Шлее и Ж.Бланшет и М.Саммерфилд, так и не разобрался с данной темой, да и как оказалось, то, что легко понимается в теории, зачастую на практике применить не получается...
Итог: реализовав бОльшую часть приложения запнулся на казалось бы пустяковой задаче.

Буду признателен любым советам по теме. Подскажите хотя бы куда копать. Использовать делегаты? Как-то прикрутить список указателей на QLabel-ы? Может QSyntaxHighlighter поможет? Или есть другой вариант, как выделить определенные буквы в списке строк в QTableView... Направьте на путь истинный...  :)

P.S.: Может посоветуете что почитать на тему делегатов, буду очень признателен.


Название: Re: html-форматирование в QTableView
Отправлено: Igors от Май 10, 2020, 12:53
Использовать делегаты? Как-то прикрутить список указателей на QLabel-ы?
А если просто в методе модели data (DisplayRole) формировать нужную html строку из исходной?


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 15, 2020, 20:03
Используй делегаты, это именно то, что тебе нужно.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 20, 2020, 20:35
Использовать делегаты? Как-то прикрутить список указателей на QLabel-ы?
А если просто в методе модели data (DisplayRole) формировать нужную html строку из исходной?

Было бы замечательно, но метод возвращает тип QVariant и к типу QLabel его не приведешь (чтобы отображать в представлении текст отформатированный в стиле html).


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 20, 2020, 20:56
Спасибо большое за подсказки!
Решил проблему следующим способом (может кому пригодится):

delegate.h:
Код
C++ (Qt)
#ifndef DELEGATE_H
#define DELEGATE_H
 
#include <QStyledItemDelegate>
 
class HtmlDelegate : public QStyledItemDelegate
{
Q_OBJECT
 
public:
HtmlDelegate(QObject *parent = 0);
 
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
 
#endif
 

delegate.cpp:
Код
C++ (Qt)
#include "delegate.h"
 
#include <QtWidgets>
 
HtmlDelegate::HtmlDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
 
void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
 
painter->save();
 
QTextDocument doc(opt.text);
 
doc.setHtml(opt.text);
 
opt.text = "";
opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &option, painter);
 
painter->translate(opt.rect.left(), opt.rect.top());
QRect clip(0, 0, opt.rect.width(), opt.rect.height());
doc.drawContents(painter, clip);
 
painter->restore();
}
 

main.cpp:
Код
C++ (Qt)
#include "delegate.h"
#include <QApplication>
#include <QtWidgets>
 
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
 
QStringListModel model;
QTableView tableView;
 
QStringList lst;
lst << "<b>Bold text.</b> Custom text." << "It's <font color=red><b>red</b></font> text. <i>Italic</i>";
 
model.setStringList(lst);
tableView.setModel(&model);
 
HtmlDelegate delegate;
tableView.setItemDelegate(&delegate);
tableView.setEditTriggers(QAbstractItemView::NoEditTriggers);
 
tableView.setWindowTitle("Html delegate");
tableView.show();
tableView.resizeColumnsToContents();
 
return app.exec();
}
 

Честно признаюсь решением не доволен... Хоть и списки для форматированного вывода в итоге должны быть небольшими, всё-равно не хочется элементами представления делать такие "тяжелые" виджеты, как QTextDocument... Но так и не придумал, как "прикрутить" к модели QTableView виджет QLabel. В сети и в справке ничего вразумительного так и не нашел, может плохо искал...


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 20, 2020, 21:26
Я помню, когда-то свой делегат чекбокса писал. Поищи, должна быть возможность QLabel заюзать.

https://www.qtcentre.org/threads/30594-QItemDelegate-with-QLabel-for-QTableView


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 06:23
Да, пробовал подобный вариант. Но переопределение методов createEditor и setEditorData предполагает, что будет редактирование модели через представление. В моем случае этого не надо. И если реализовать свой делегат подобным образом, то текст в представлении отображается некорректно, виджет QLabel как-бы накладывается на текст (происходит дублирование).

В моем случае это выглядит так:
(https://a.radikal.ru/a14/2005/4e/4498266ba93b.png) (https://radikal.ru)


Код следующий:

delegate.h:
Код
C++ (Qt)
#ifndef DELEGATE_H
#define DELEGATE_H
 
#include <QStyledItemDelegate>
 
class HtmlDelegate : public QStyledItemDelegate
{
   Q_OBJECT
 
public:
HtmlDelegate(QObject *parent = 0);
 
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
 
void setEditorData(QWidget* editor, const QModelIndex& index) const;
};
 
#endif
 

delegate.cpp:
Код
C++ (Qt)
#include "delegate.h"
 
#include <QtWidgets>
 
HtmlDelegate::HtmlDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
 
 
QWidget* HtmlDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const
{
QLabel* label = new QLabel(parent);
 
return label;
}
 
void HtmlDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
QString value = index.model()->data(index, Qt::DisplayRole).toString();
 
QLabel* label = static_cast<QLabel*>(editor);
 
label->setText(value);
}
 
 

main.cpp:
Код
C++ (Qt)
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
 
QStringListModel model;
QTableView tableView;
 
QStringList lst;
 
lst << "<b>First</b> html text" << "Second html <i>text</i>";
 
model.setStringList(lst);
tableView.setModel(&model);
 
HtmlDelegate delegate;
tableView.setItemDelegate(&delegate);
tableView.setEditTriggers(QAbstractItemView::NoEditTriggers);
 
tableView.setWindowTitle("Html delegate");
tableView.show();
tableView.resizeColumnsToContents();
for(int i=0; i<lst.count(); ++i) {
tableView.openPersistentEditor(model.index(i)); // view delegate all time
}
 
return app.exec();
}
 


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 07:31
А куда ты дел все, что связано с QStyleOptionViewItem? Ты же должен не только создать виджет, но и корректно его разместить.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 09:20
А куда ты дел все, что связано с QStyleOptionViewItem? Ты же должен не только создать виджет, но и корректно его разместить.

Уважаемый Пантер, не очень понимаю, что ты имеешь ввиду... Хотя бы пару строчек кода, или ссылку на пример... Куда и как именно я должен разместить "все, что связано с QStyleOptionViewItem"?


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 09:28
tamplier, у тебя же выше был код HtmlDelegate::paint. Для QLabel тоже нужно реализовать этот метод и правильно разместить виджет.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 09:37
tamplier, у тебя же выше был код HtmlDelegate::paint. Для QLabel тоже нужно реализовать этот метод и правильно разместить виджет.

Да. Я понимаю, что это логично, именно метод paint использовать для отображения QLabel в представлении... Но у меня затык в том, что QLabel не содержит метод drawContents, и аналогичного я не нашел. Не могу понять как отрисовать виджет QLabel в итеме...  :-[


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 10:17
https://doc.qt.io/qt-5/qwidget.html#grab
https://doc.qt.io/qt-5/qwidget.html#render
Может, куда-то сюда покопать...


Название: Re: html-форматирование в QTableView
Отправлено: Igors от Май 21, 2020, 11:14
Было бы замечательно, но метод возвращает тип QVariant и к типу QLabel его не приведешь (чтобы отображать в представлении текст отформатированный в стиле html).
А просто QString в html?


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 11:20
Было бы замечательно, но метод возвращает тип QVariant и к типу QLabel его не приведешь (чтобы отображать в представлении текст отформатированный в стиле html).
А просто QString в html?
QLabel нужен для рендеринга.


Название: Re: html-форматирование в QTableView
Отправлено: ViTech от Май 21, 2020, 11:27
Не могу понять как отрисовать виджет QLabel в итеме...  :-[

QAbstractItemView::setIndexWidget (https://doc.qt.io/qt-5/qabstractitemview.html#setIndexWidget) пробовали?


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 11:33
https://doc.qt.io/qt-5/qwidget.html#grab
https://doc.qt.io/qt-5/qwidget.html#render
Может, куда-то сюда покопать...

Спасибо ОГРОМНОЕ! Очень помог!


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 11:42
https://doc.qt.io/qt-5/qwidget.html#grab
https://doc.qt.io/qt-5/qwidget.html#render
Может, куда-то сюда покопать...

Спасибо ОГРОМНОЕ! Очень помог!
Не забудь сюда запостить код рабочего решения.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 11:46
Благодаря тому, что Пантер дал мне правильную подсказку (указал на нужный метод), получилось следующее:

htmldelegat.h
Код
C++ (Qt)
#ifndef HTMLDELEGATE_H
#define HTMLDELEGATE_H
 
#include <QStyledItemDelegate>
 
class HtmlDelegate : public QStyledItemDelegate
{
   Q_OBJECT
 
public:
   HtmlDelegate(QObject *parent = 0);
 
   void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
 
#endif
 

htmldelegat.cpp
Код
C++ (Qt)
#include <QtWidgets>
#include "htmldelegate.h"
 
HtmlDelegate::HtmlDelegate(QObject *parent)
   : QStyledItemDelegate(parent)
{
}
 
void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
   QStyleOptionViewItem opt = option;
   initStyleOption(&opt, index);
 
   painter->save();
 
   QLabel doc(opt.text);
 
   doc.setText(opt.text);
 
   opt.text = "";
   opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &option, painter);
 
   painter->translate(opt.rect.left(), opt.rect.top());
   QRect clip(0, 0, opt.rect.width(), opt.rect.height());
 
   doc.render(painter, QPoint(0,0), QRegion(QRect(clip)), QWidget::DrawChildren); // !!!!!
 
   painter->restore();
}
 

main.cpp
Код
C++ (Qt)
#include "htmldelegate.h"
#include <QApplication>
#include <QtWidgets>
 
int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
 
   QStringListModel model;
   QTableView tableView;
 
   QStringList lst;
 
   lst << "<b>First</b> html text"
       << "Se<font color=red>con</font>d html <i>text</i>";
 
   model.setStringList(lst);
   tableView.setModel(&model);
 
   HtmlDelegate delegate;
   tableView.setItemDelegate(&delegate);
   tableView.setEditTriggers(QAbstractItemView::NoEditTriggers);
 
   tableView.setWindowTitle("Html delegate");
   tableView.show();
   tableView.resizeColumnsToContents();
 
   return app.exec();
}
 


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 11:48
Наверное это единственный форум, где решена проблема отображения QLabel в QTableView  :). Тему можно закрывать.


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 11:54
Отлично! Пусть теперь кешируется в поисковиках. :)


Название: Re: html-форматирование в QTableView
Отправлено: Igors от Май 21, 2020, 12:12
QLabel нужен для рендеринга.
А нужен ли? Если просто так дать таблице/ячейке html строку, она что, эту строку не отрендерит ?  Стоит проверить


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 12:43
QLabel нужен для рендеринга.
А нужен ли? Если просто так дать таблице/ячейке html строку, она что, эту строку не отрендерит ?  Стоит проверить
Нет, не отрендерит. Иначе не предлагали бы делегаты.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 12:44
QLabel нужен для рендеринга.
А нужен ли? Если просто так дать таблице/ячейке html строку, она что, эту строку не отрендерит ?  Стоит проверить

У меня не рендерила... Это первое, что приходит в голову, стал бы я заморачиваться делегатами... Где-то встречал, по-моему, решение для QTableWidget, там есть вариант без QLabel (могу ошибаться, много тем перелопатил за это время), но для его предка QTableView нет такой возможности...

Вообще, хотелось бы решение, которое выделяет буквы в строках представления не используя при этом виджеты...


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 13:04
И пока тему не закрыли, расскажу пару моментов. Перенес этот делегат HTML в свой проект и при "внедрении" надо учесть следующие аспекты:
1) Лучше инициализировать делегат в куче (т.е. HtmlDelegate *delegate = new HtmlDelegate;) Чтобы при выходе из модуля, где он был инициализирован и установлен для QTableView он не пропадал (иначе предаставление будет отображать пустые элементы)
2) Чтобы текст не "прилипал" к левому и правому краю ячейки в представлении необходимо в реализацию делегата (файл htmldelegate.cpp метод paint) немного подправить метод render:

Код
C++ (Qt)
//............
painter->translate(opt.rect.left(), opt.rect.top());
QRect clip(0, 0, opt.rect.width(), opt.rect.height());
 
lbl.render(painter, QPoint(5,5), QRegion(QRect(clip)), QWidget::DrawChildren); // QPoint(5,5)
//.........
 

Немного топорно, конечно, прибавлять по 5 пикселей отступ с каждой стороны, но пока ничего лучше не придумал (opt.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter - почему-то не сработало...).


Название: Re: html-форматирование в QTableView
Отправлено: ViTech от Май 21, 2020, 13:13
Вообще, хотелось бы решение, которое выделяет буквы в строках представления не используя при этом виджеты...

Можно в исходниках посмотреть, как QLabel текст рисует. Навскидку, QLabel::paintEvent() (https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlabel.cpp.html#_ZN6QLabel10paintEventEP11QPaintEvent).


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 13:34
И пока тему не закрыли, расскажу пару моментов. Перенес этот делегат HTML в свой проект и при "внедрении" надо учесть следующие аспекты:
1) Лучше инициализировать делегат в куче (т.е. HtmlDelegate *delegate = new HtmlDelegate;) Чтобы при выходе из модуля, где он был инициализирован и установлен для QTableView он не пропадал (иначе предаставление будет отображать пустые элементы)
2) Чтобы текст не "прилипал" к левому и правому краю ячейки в представлении необходимо в реализацию делегата (файл htmldelegate.cpp метод paint) немного подправить метод render:

Код
C++ (Qt)
//............
painter->translate(opt.rect.left(), opt.rect.top());
QRect clip(0, 0, opt.rect.width(), opt.rect.height());
 
lbl.render(painter, QPoint(5,5), QRegion(QRect(clip)), QWidget::DrawChildren); // QPoint(5,5)
//.........
 

Немного топорно, конечно, прибавлять по 5 пикселей отступ с каждой стороны, но пока ничего лучше не придумал (opt.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter - почему-то не сработало...).


Методом научного тыка пришел к такой формуле (она добавляет небольшой отступ от левого края ячейки, и по вертикали получается динамика, если увеличивать/уменьшать высоту строки, то текст располагается всегда примерно по центру).

Код
C++ (Qt)
lbl.render(painter, QPoint(5,opt.rect.height()/2 - 7), QRegion(QRect(clip)), QWidget::DrawChildren);
 


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 13:37
Вообще, хотелось бы решение, которое выделяет буквы в строках представления не используя при этом виджеты...

Можно в исходниках посмотреть, как QLabel текст рисует. Навскидку, QLabel::paintEvent() (https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlabel.cpp.html#_ZN6QLabel10paintEventEP11QPaintEvent).

Честно говоря столько времени "убил" на эту тему... А еще нужно сделать сохранение в разных форматах и справку... Может когда-нибудь я к этому вернусь.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 14:13
Товарищи программисты  ;D
Кое-что протестировал сейчас и оказалось...
При использовании стандартного делегата представления QTableView, приложение при загрузке списка (в модель QStringListModel) размером 2 млн. строк (кириллица) использует оперативной памяти БОЛЬШЕ(!!!), чем, когда используется делегат HtmlDelegate (описанного в этой теме)   :o
Проверил несколько раз и вышло, что с форматированием html приложение кушает оперативы МЕНЬШЕ, чем когда подгружаешь те же списки и они отображаются в представлении с неотформатированными тегами (в первом случае заняло ~346 Мб, во втором (без HTML форматирования) - ~348 Мб).
Следовательно возможность HTML-форматирования в представлении без использования QLabel теряет свою актуальность  :)

P.S.: тест проводился на Windows XP SP3. Именно под нее и пишется программа.


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 14:15
2 MB разницы? Ты уверен, что стоит на это обращать внимание?


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 14:24
2 MB разницы? Ты уверен, что стоит на это обращать внимание?

Пантер, ты видимо не понял, я думал, что при использовании в представлении QLabel-ов будет сильно "кушаться" оперативка. И просто решил проверить на ИСХОДНЫХ списках насколько больше. Подгрузил не самые большие (всего два списка по 1 млн. строк, у меня используются де модели и два представления). А оказалось, что она, оперативка, "кушается" не больше, а МЕНЬШЕ(!!!), чем раньше, когда я использовал стандартный делегат (по умолчанию). Вообще, форматирование строк мне нужно тогда, когда их (строк) станет много меньше, как правило пару десятков, а тут выходит, что и при исходном большом их количестве памаять расходуется так же, даже меньше...

Видимо это особенности реализации представлений в Qt...


Название: Re: html-форматирование в QTableView
Отправлено: Пантер от Май 21, 2020, 14:30
У тебя делегаты создаются только для видимых строк, так что пофиг 2 млн их или 20 штук.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 14:34
2 MB разницы? Ты уверен, что стоит на это обращать внимание?

Поправил, мегабайты неправильно написал...


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 14:36
У тебя делегаты создаются только для видимых строк, так что пофиг 2 млн их или 20 штук.

Ну, я новичок, не знал об этом. Делегаты для меня непростая тема...


Название: Re: html-форматирование в QTableView
Отправлено: ViTech от Май 21, 2020, 16:47
Честно говоря столько времени "убил" на эту тему... А еще нужно сделать сохранение в разных форматах и справку... Может когда-нибудь я к этому вернусь.

Там может делов на:
Код
C++ (Qt)
style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole());

Если не смущает, что в paint() каждый раз создаётся и уничтожается QLabel, то можно не заморачиваться. Но меня бы это, как минимум, насторожило.


Название: Re: html-форматирование в QTableView
Отправлено: tamplier от Май 21, 2020, 18:38
Честно говоря столько времени "убил" на эту тему... А еще нужно сделать сохранение в разных форматах и справку... Может когда-нибудь я к этому вернусь.

Там может делов на:
Код
C++ (Qt)
style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole());

Если не смущает, что в paint() каждый раз создаётся и уничтожается QLabel, то можно не заморачиваться. Но меня бы это, как минимум, насторожило.

Ну, быстродействие не "упало", и т.к. QLabel и создается, и уничтожается, то меня это не смущает.

Если приведете здесь реальный рабочий пример с методом drawItemText, то, полагаю, многие люди ищущие в сети ответ на вопрос "как форматировать текст" или "как отображать HTML-форматирование" в представлении (например в QTableView) будут Вам весьма благодарны.


Название: Re: html-форматирование в QTableView
Отправлено: Igors от Май 22, 2020, 10:25
Если не смущает, что в paint() каждый раз создаётся и уничтожается QLabel, то можно не заморачиваться. Но меня бы это, как минимум, насторожило.
Да, это не есть хорошо. Наивное предположение: drawText рисует html, drawItemText нет. Тогда стоит попробовать просто drawText в делегате (без виджета)