Russian Qt Forum
Сентябрь 29, 2024, 02:16 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Выравнивание виджета в ячейке таблицы  (Прочитано 11391 раз)
dolphinik
Гость
« : Ноябрь 04, 2008, 18:46 »

Добрый день.

Подскажите как можно выровнять по-центру QCheckBox в ячейке QTableWidget?
Или как сделать то же самое с обычным QTableWidgetItem с установленной опцией setCheckState ?
Опция setTextAlignment не помагает.

Вот краткие примеры одного и другого случаев

1
Код:
            
item = QtGui.QTableWidgetItem()
item.setCheckState(value == True and QtCore.Qt.Checked or QtCore.Qt.Unchecked )
widget.setItem(x, y, item)

2
Код:
item = QtGui.QCheckBox()
item.setCheckState(value == True and QtCore.Qt.Checked or QtCore.Qt.Unchecked )
widget.setCellWidget(x,y, item)

Измучился уже вконец...
Записан
EhTemka
Гость
« Ответ #1 : Ноябрь 13, 2008, 13:37 »

Как выровнить QCheckBoxы не знаю, но я как то писал таблицу с большим количеством ячеек, где должны были эти чекбоксы рисоваться. Причем они должны были срабатывать сразу, то бишь по одному клику на ячейку(без предварительного клика для открытия редактирования). Там у меня все рисовалось по центру, причем не сами QCheckBoxы (так как это тормозило), а их изображение.

Если тебя это устроит могу дать код. Там всего то надо переопределить паинт в делегате и событие клика в таблице.
Записан
kirill
Гость
« Ответ #2 : Январь 13, 2010, 09:52 »

Добавлять не QCheckBox а Виджет с Лэйаутом в который помещен КуЧекБокс
Код
C++ (Qt)
  QCheckBox * check = new QCheckBox(this);
  QWidget * w = new QWidget(this);
  QHBoxLayout * hlw = new QHBoxLayout;
  hlw->setMargin(0);
  hlw->addWidget(check, 0, Qt::AlignCenter);
  w->setLayout(hlw);
 
  view_->setModel(Model_);
 
  view_->setIndexWidget(Model_->index(0, 0), w);
 
Записан
sibmail
Гость
« Ответ #3 : Февраль 08, 2011, 11:39 »

а как потом проверить состояние чекбокса?
Записан
vfilatov
Гость
« Ответ #4 : Февраль 09, 2011, 16:15 »

По поводу выравнивания чекбоксов есть вариант решения в факе, но он кривоватый (попробуйте - увидите сами), поэтому хочу предложить свой, модифицированный.
Итак, нам необходимо определить свой класс делегата:

Код:
class ItemDelegate : public QItemDelegate


Отрисовка

Чтобы нарисовать чекбокс по центру ячейки достаточно переопределить метод drawCheck. В него передаётся параметр rect, мы его модифицируем, сместив в центр и вызовем метод базового класса:

Код:
void ItemDelegate::drawCheck(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, Qt::CheckState state) const {
QRect rect_copy(rect);

if(!rect_copy.isNull()) {
rect_copy.moveCenter(option.rect.center());
}

QItemDelegate::drawCheck(painter, option, rect_copy, state);
}

Если сейчас сделать и запустить тестовый проект с таким кодом, то мы увидим, что чекбокс рисуется по центру, как мы и хотели... до тех пор, пока мы не выделим ячейку. А при выделении ячейки цвет выделения полностью закрашивает наш чекбокс. Происходит это потому, что переместив чекбокс в центр ячейки, мы теперь его рисуем в области, где Qt рисует текст. Рисование текста происходит это в функции drawDisplay, которая вызывается после drawCheck. В ней Qt заливает область цветом выделения, затем рисует текст. Текст у нас пустой (иначе зачем бы мы стали выравнивать чекбокс по центру), однако заливка цветом выделения всё равно происходит. Зачем Qt так делает, я не понял, ведь при отрисовке ячейки первым делом вызывается функция  drawBackground, которая заливает всю ячейку нужным цветом, поэтому в drawDisplay происходит повторная заливка части уже залитой области.
Проще всего избавиться от этого поведения - переопределить функцию drawDisplay просто оставив её пустой:

Код:
void ItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text) const {
}

Теперь можно видеть, что чекбоксы у нас рисуются по центру как в выделенных, так и в не выделенных ячейках. Если вы устанавливаете делегат только на столбцы с чекбоксами, то можно сразу переходить к части Редактирование. Однако я, как правило, устанавливаю один делегат на всю таблицу и предоставляю ему самому разбираться, где чекбоксы, где нет и правильно отрисовывать любые ячейки. Для этого придётся ещё немного поработать.
Главное что нам нужно знать - рисуем ли мы сейчас ячейку с чекбоксом или ячейку другого типа, а точнее, нам достаточно знать, присутствует ли в отрисовываемой ячейке текст или нет. Ячейки с текстом, даже чекбоксы, нам надо рисовать как обычно. К сожалению, в методе drawCheck узнать это, похоже, нельзя (или я не знаю как), поэтому необходимо переопределить функцию paint:

Код:
void ItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
_draw_display = !index.data(Qt::DisplayRole).value<QString>().isEmpty();

QItemDelegate::paint(painter, option, index);
}

Последовательность вызовов здесть такая: при отрисовке ячейки сначала вызывается функция paint, в которой мы устанавливаем флажок наличия в ячейке текста. Потом мы вызываем функцию базового класса из которой вызовутся наши переопределённые drawCheck и drawDisplay.
Переменная _draw_display должна иметь тип mutable bool, потому что нам приходится модифицировать её в константной функции paint.
Теперь drawCheck будет выглядить таким образом:

Код:
void ItemDelegate::drawCheck(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, Qt::CheckState state) const {
QRect rect_copy(rect);

if(!_draw_display) {
if(!rect_copy.isNull()) {
rect_copy.moveCenter(option.rect.center());
}
}

QItemDelegate::drawCheck(painter, option, rect_copy, state);
}

А drawDisplay таким:

Код:
void ItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text) const {
if(_draw_display) {
QItemDelegate::drawDisplay(painter, option, rect, text);
}
}

Т.е. логика простейшая: если текст в ячейке есть, то мы ничего не трогаем, всю работу делают базовые функции (чекбоксы с текстом мы не выравниваем, я думаю, это логично). Если текста нет, вмешиваемся в процесс отрисовки, пытаясь выровнять чекбокс по центру. Причём здесь даже не важно, является ли отрисовываемая ячейка чекбоксом, всё будет работать правильно в любом случае.
Ну вот с отрисовкой и всё, букв много, но на самом деле всё просто, всю работу выполняют три последних фрагмента кода (переопределённые paint, drawCheck и  drawDisplay).


Редактирование

Если мы оставим всё как есть, то состояние чекбокса будет меняться при клике не в то место, где мы его отрисовали теперь (по центру), а в то, где Qt думает, что он до сих пор находится (слева). Обработку пользовательского ввода Qt осуществляет в функции editorEvent, которую можно (и нужно) переопределить. К сожалению, область, где находится чекбокс, в эту функцию не передаётся, поэтому нет возможности модифицировать входные параметры и вызвать базовую функцию. Нам ничего не остаётся, кроме как скопипастить код библиотеки, закомментировать строки, где определяется положение чекбокса и вставить туда свои. Получается так:

Код:
bool ItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) {
const bool draw_display = !index.data(Qt::DisplayRole).value<QString>().isEmpty();

if(draw_display) {
return QItemDelegate::editorEvent(event, model, option, index);
}
else {
Q_ASSERT(event);
Q_ASSERT(model);

// make sure that the item is checkable
Qt::ItemFlags flags = model->flags(index);
if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
|| !(flags & Qt::ItemIsEnabled))
return false;

// make sure that we have a check state
QVariant value = index.data(Qt::CheckStateRole);
if (!value.isValid())
return false;

// make sure that we have the right event type
if ((event->type() == QEvent::MouseButtonRelease)
|| (event->type() == QEvent::MouseButtonDblClick)) {
//QRect checkRect = check(option, option.rect, Qt::Checked);
//QRect emptyRect;
//doLayout(option, &checkRect, &emptyRect, &emptyRect, false);

QStyleOptionButton styleOptionButton;
QRect checkRect = QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &styleOptionButton);
checkRect.moveCenter(option.rect.center());

QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos()))
return false;

// eat the double click events inside the check rect
if (event->type() == QEvent::MouseButtonDblClick)
return true;

} else if (event->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
&& static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
return false;
} else {
return false;
}

Qt::CheckState state;
if ( flags & Qt::ItemIsTristate ) {
state = static_cast<Qt::CheckState>( (value.toInt() + 1) % 3 );
} else {
state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);
}

return model->setData(index, state, Qt::CheckStateRole);
}
}

Теперь мы можем безопасно устанавливать такой делегат на всю таблицу и наслаждаться выровненными по центру чекбоксами. Ну, по крайней мере у меня всё работает как часики Улыбающийся
« Последнее редактирование: Февраль 09, 2011, 16:17 от vfilatov » Записан
blood_shadow
Гость
« Ответ #5 : Март 30, 2011, 15:30 »

По поводу выравнивания чекбоксов есть вариант решения в факе, но он кривоватый (попробуйте - увидите сами), поэтому хочу предложить свой, модифицированный
сам долго долбался решение по проще, может кому-то пригодится:
Код
C++ (Qt)
  QStyleOptionButton *buttonOption = new QStyleOptionButton;
 
  buttonOption->initFrom(widget);
  QRect centeredRect = option.rect;
  buttonOption->rect = centeredRect.adjusted(
     centeredRect.width() / 2
     - QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth) / 2, 0, 0, 0);
 
  buttonOption->state |= QStyle::State_On;
 
  QApplication::style()->drawControl(QStyle::CE_CheckBox, buttonOption, painter, widget);
 

QStyle::PM_IndicatorWidth это ширина в пикселях нашего чекбокса(без текста),
который нужно перевести с пикселей с помощью pixelMetric()
результат - идеальное выравнивание даже при растяжении
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.117 секунд. Запросов: 21.