LINUX.ORG.RU

Делегаты в Qt 5.6.1

 , ,


0

1

Я реализовал свою модель, унасленованную от QAbstractListModel, которая отображает на QListView структуру следующего вида:

struct MyCustomStruct {
	QIcon icon;
	QString str1;
	QString str2;
} ;

QListView настроен таким образом, что отображает список в виде значков по сетке, как папки в проводнике (вверху значек, под ним надпись). В моей структуре надписей должно быть две, кроме того, хотелось бы писать их разным шрифтом, поэтому вариант одной строки вида"str1\nstr2" сразу отпадает. Для рисования я реализовал свой делегат, унаследованный от QStyledItemDelegate. В принципе все работает, но не устраивает рисование рамки вокруг текущего выделенного элемента. По хорошему, должно быть так, а выглядит оно вот так. Помогите заставить рамку снова выглядеть нативно. В исходниках она рисуется в mydelegate.cpp:42, этот код я подсмотрел в исходниках Qt в qcommonstyle.cpp, но работает почему-то не так, как задумано. Посмотреть, как оно должно выглядеть, можно просто закомментировав строку mainwindow.cpp:17

Исходники примера вот тут

★★★★

Выложи ли бы сорцы делегата сюда. У меня хром блочит rghost.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon
// mydelegate.h
#ifndef MYDELEGATE_H
#define MYDELEGATE_H

#include <QStyledItemDelegate>

class MyDelegate : public QStyledItemDelegate
{
	Q_OBJECT
	// QAbstractItemDelegate interface
public:
	void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
	QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

#endif // MYDELEGATE_H

// mydelegate.cpp
#include "mydelegate.h"
#include "mymodel.h"
#include <QApplication>
#include <QPainter>

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	if (!index.isValid()) {
		return;
	}

	QStyleOptionViewItem opt = option;
	initStyleOption(&opt, index);
	QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
	QRect rect = opt.rect;

	const MyModel *model = static_cast<const MyModel *>(index.model());
	MyCustomStruct item = model->getItem(index);

	// рисуем иконку
	painter->drawPixmap(rect.left() + (rect.width() - 32) / 2,
						rect.top() + 4,
						item.icon.pixmap(32, 32));

	// рисуем первую строку
	painter->drawText(rect.left(),
					  rect.top() + 32 + 4,
					  rect.width(),
					  opt.fontMetrics.height() + 4,
					  opt.displayAlignment,
					  item.str1);

	// рисуем вторую строку
	painter->drawText(rect.left(),
					  rect.top() + 32 + 4 + opt.fontMetrics.height() + 4,
					  rect.width(),
					  opt.fontMetrics.height() + 4,
					  opt.displayAlignment,
					  item.str2);

	// рисуем рамку выделения (!!!!!!!)
	if (opt.state & QStyle::State_HasFocus) {
		painter->save();
		QStyleOptionFocusRect o;
		o.QStyleOption::operator=(opt);
		o.rect = option.rect.adjusted(1, 1, -1, -1);
		o.state |= QStyle::State_KeyboardFocusChange;
		o.state |= QStyle::State_Item;
		QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled)
					  ? QPalette::Normal : QPalette::Disabled;
		o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected)
									 ? QPalette::Highlight : QPalette::Window);
		style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, opt.widget);
		painter->restore();
	}

}

QSize MyDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	if (!index.isValid()) {
		return QSize(0, 0);
	}

	const MyModel *model = static_cast<const MyModel *>(index.model());
	MyCustomStruct item = model->getItem(index);
	QFontMetrics qfm = option.fontMetrics;

	QSize s = QStyledItemDelegate::sizeHint(option, index);
	s.setWidth(qMax<int>(qfm.width(item.str1), qfm.width(item.str1)) + 4);
	s.setHeight(4 + 32 + ((qfm.height() + 4) * 2));
	return s;
}

WRG ★★★★ ()
Ответ на: комментарий от RazrFalcon

Он рисует всё - и иконку и текст, и рамку выделения сразу. Иконку и текст я хочу рисовать сам. Кусок моего делегата, который рисует рамку, выдран как раз из места в коде Qt, где рисуется CE_ItemViewItem.

Более того, CE_ItemViewItem непонятно как располагает текст в пределах отведенного ему option.rect, почти по середине. Т.е. когда я меняю sizeHint, чтобы выделить больше места, CE_ItemViewItem перемещает текст в середину свободного пространства под иконкой. Я мог бы использовать CE_ItemViewItem чтобы нарисовать иконку и первую строку, а вторую дорисовывал бы сам, но это все портит.

WRG ★★★★ ()
Ответ на: комментарий от WRG

Так CE_ItemViewItem рисует как вам надо? Тогда отключите текст и иконку через opt и всё. Он будет тогда рисовать только рамку.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

CE_ItemViewItem определяет размер рамки по иконке и тексту, которые он рисует. Если их отключить через opt, то рамка даже не видна, видимо нулевого размера получается. Придумал костыль - заполняю opt.text нужным количеством пробелов, которое по ширине будет совпадать с самой длинной из двух моих строк, тогда всё рамка рисуется как надо, а сверху уже я рисую иконку и строки. Проблема этого способа в адской костыльности и быстродействии.

WRG ★★★★ ()
Ответ на: комментарий от WRG

rect можно самому рассчитывать. Других вариантов я не знаю, по моему это не такой уж и костыль. Быстродействие вообще не при чём.

Как минимум я именно так и делаю. Других вариантов не встречал.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Я бы расчитал его сам, но в CE_ItemViewItem он его все равно пересчитывает. Т.е. используется не тот, что я передал ему в option.rect,а свой, расчитанный вот так:

// draw the focus rect
             if (vopt->state & QStyle::State_HasFocus) {
                QStyleOptionFocusRect o;
                o.QStyleOption::operator=(*vopt);
                o.rect = proxy()->subElementRect(SE_ItemViewItemFocusRect, vopt, widget); //тут
                o.state |= QStyle::State_KeyboardFocusChange;
                o.state |= QStyle::State_Item;
                QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled)
                              ? QPalette::Normal : QPalette::Disabled;
                o.backgroundColor = vopt->palette.color(cg, (vopt->state & QStyle::State_Selected)
                                             ? QPalette::Highlight : QPalette::Window);
                proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, p, widget);
            }

Быстродействием страдает не именно этот способ рисования рамки, а мой костыль с составлением строки, которая содержит нужное число пробелов. Причем это заметно на глаз, при ресайзе окна. Можно, конечно, где-нибудь «закэшировать» эту строку с пробелами, вероятно так и поступлю если не придумаю ничего лучше, составлять ее каждый раз это оверхед.

WRG ★★★★ ()

хотелось бы писать их разным шрифтом, поэтому вариант одной строки вида"str1\nstr2" сразу отпадает.

А там разве нельзя html? Типа,

<span style="...">Line1</span><br>
<span style="...">Line2</span>
Тогда, возможно, и не придётся городить свой делегат.

Beewek ()

Глупый вопрос, но почему не QML? Там это делается легко и изящно за 5 минут.

CrossFire ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Во-первых у Quick Controls 1 есть поддержка стандартных тем, хотя может и чуть хуже чем у виджетов, не сравнивал. У controls 2 есть поддержка Universal стиля из win 8/10. Во-вторых, в данном конкретном вопросе все равно рисуется руками, но на порядок сложнее.

CrossFire ★★★★★ ()
Ответ на: комментарий от CrossFire

Во-первых у Quick Controls 1 есть поддержка стандартных тем

deprecated же

У controls 2 есть поддержка Universal стиля из win 8/10

Не виндой едины.

Во-вторых, в данном конкретном вопросе все равно рисуется руками, но на порядок сложнее.

Рисуется посредством системной темы.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

deprecated же

Пруф можно?

Не виндой едины.

На маке рамка рисуется совсем по-другому — иконка отдельно, текст отдельно, разными цветами. Это учитывается в данном случае?

CrossFire ★★★★★ ()
Последнее исправление: CrossFire (всего исправлений: 2)
Ответ на: комментарий от CrossFire

Пруф можно?

Наличие версии с цифрой 2 не пруф? У них даже в доке написано, что QC1 was a mistake.

Это учитывается в данном случае?

Это к ТС.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Наличие версии с цифрой 2 не пруф?

Нет

У них даже в доке написано, что QC1 was a mistake.

Где?

CrossFire ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Где там что-нибудь про deprecated или «was a mistake»?

Берем полную цитату:

Qt Quick Controls were originally developed to support desktop platforms, with mobile and embedded support coming shortly afterwards. They have a very broad scope, in that they provide a styling system flexible enough to allow the development of applications that have either a platform-dependent or platform-independent style.

On embedded systems, where the hardware has limited resources, this approach can be inefficient. Qt Quick Controls 2 were designed to solve this problem, using benchmarks to guide the development.

Тут явно написано, что Qt Quick Controls 1 разработаны для настольных приложений, а мобильных и встроенных работают недостаточно хорошо, и Qt Quick Controls 2 созданы, чтобы исправить это.

Таким образом нигде не написано, что это была ошибка, что первые уже устарели — у них просто разные области применения.

CrossFire ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.