QTableWidget setCellWidget (QWidget *) Несовместимое поведение с выбором ячейки и фокусом - PullRequest
0 голосов
/ 11 июля 2019

Я сделал подкласс QTableWidget и хотел, чтобы некоторые ячейки имели QPushButtons в качестве виджетов ячеек, так как я сделал довольно сильно стилизованную кнопку, используя QPropertyAnimations, а что нет, и действительно хотел встроить виджет вклетка.Я использовал функцию setCellWidget(QWidget* widget), которая является частью QTableWidget, и она была почти идеальной.Так как я делаю некоторые подклассы QStyledItemDelegate рисования классов на элементах ячейки моей таблицы, я рисую некоторые линии границ, которые, кажется, немного конфликтуют с размером виджета ячейки.

Ранее кто-то спрашивал, как центрировать QCheckBox в ячейке QTableWidget's, поэтому она не смещена с левой стороны.Ответ на этот вопрос был по существу следующим:

  1. Создайте QWidget
  2. Создайте QWidget, который вы хотите центрировать
  3. Создайте QH/VLayout иустановите макет верхнего уровня QWidget's таким:
  4. Добавьте вспомогательный виджет, который вы хотите центрировать в этом макете
  5. На QTableWidget используйте функцию setCellWidget и установите его длястрока и столбец с верхним уровнем QWidget и вуаля, у вас есть центрированный QWidget в ячейке, которой вы можете манипулировать и выравнивать, как вам захочется

Отлично, это визуально сработало ...Однако я заметил некоторые неприятные побочные эффекты этого.Навигация клавиш со стрелками, кажется, сломалась, и я больше не могу нажимать клавишу Enter или клавишу пробела, чтобы «щелкнуть / нажать» QPushButton, который я встроил в управляющий QWidget для этой конкретной ячейки.Кажется, что смена фокуса внутренне что-то портит на QTableWidget при попытке отойти от клетки с помощью клавиш со стрелками на клавиатуре.

Я читал в Интернете, что некоторые люди сказали отключить setTabKeyNavigation(bool)на столе, чтобы исправить некоторые подобные проблемы с навигацией после установки фокуса ... Это ничего не сделало в моем случае.Я сделал минимальный компилируемый пример, который показывает случаи поведения

TableWidget.h:

#ifndef TABLEWIDGET_H
#define TABLEWIDGET_H

#include "button.h"
#include "widget.h"

#include <QTableWidget>
#include <QDebug>

class TableWidget : public QTableWidget{
    Q_OBJECT

public:
    TableWidget(QWidget* parent = nullptr);
    Widget *createWidget();
    Button *createButton();
    void setTableCell(QWidget *selecteditem);
};

#endif // TABLEWIDGET_H

TableWidget.cpp:

#include "tablewidget.h"

TableWidget::TableWidget(QWidget* parent) : QTableWidget(parent){
    setFixedSize(750, 500);

    setColumnCount(5);

    for(int i = 0; i < 5; ++i){
        insertRow(rowCount());
        for(int j = 0; j < columnCount(); ++j){
            QTableWidgetItem* item = new QTableWidgetItem;
            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
            setItem(j, i, item);
        }
    }

    setCellWidget(0, 0, createButton());
    setCellWidget(2, 0, createWidget());
}

Button* TableWidget::createButton(){
    Button* button = new Button;
    connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection);
    return button;
}

Widget* TableWidget::createWidget(){
    Button* button = new Button;
    connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection);

    return new Widget(button);
}

//Helper to make keyboard focus more intuitive for cell widgets versus regular items
void TableWidget::setTableCell(QWidget* selecteditem){
    //Find the sender in the table
    for(int row = 0; row < rowCount(); ++row){
        for(int col = 0; col < columnCount(); ++col){
            if(cellWidget(row, col) == selecteditem){
                qDebug() << "TableWidget::setTableCell";
                setCurrentCell(row, col);
                setCurrentItem(this->item(row, col));
                setCurrentIndex(this->indexFromItem(this->item(row, col)));
                return;
            }
        }
    }
}

Button.h:

#ifndef BUTTON_H
#define BUTTON_H

#include <QPushButton>
#include <QFocusEvent>
#include <QDebug>

class Button : public QPushButton{
    Q_OBJECT
public:
    Button(QWidget *parent = nullptr);

    void focusIn(QFocusEvent *event);
signals:
    void focusReceived();
public slots:
    bool event(QEvent* e);
};

#endif // BUTTON_H

Button.cpp:

#include "button.h"

Button::Button(QWidget* parent) : QPushButton(parent){
    setStyleSheet(QString("background-color: solid rgba(255, 0, 0, 75);"));
}

bool Button::event(QEvent* event){
    switch(event->type()){
        case QEvent::FocusIn:
            focusIn(static_cast<QFocusEvent*>(event));
            return true;
            break;
        default:
            break;
    }

    return QWidget::event(event);
}

void Button::focusIn(QFocusEvent* event){
    qDebug() << "Button::focusIn";
    emit focusReceived();
    QPushButton::focusInEvent(event);
}

Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFocusEvent>
#include <QHBoxLayout>
#include <QPointer>
#include "button.h"

class Widget : public QWidget{
    Q_OBJECT
public:
    Widget(Button* button, QWidget *parent = nullptr);

public slots:
    bool event(QEvent* event);
    void focusIn(QFocusEvent *event);

signals:
    void focusReceived();

protected:
    QPointer<QHBoxLayout> m_hLayout;
    QPointer<Button>      m_button;
};

#endif // WIDGET_H

Widget.cpp:

#include "widget.h"

Widget::Widget(Button* button, QWidget* parent) : QWidget(parent){
    m_button = button;
    setStyleSheet(QString("background-color: solid rgba(0, 0, 0, 0);"));
    m_hLayout = new QHBoxLayout;
    setLayout(m_hLayout);
    m_hLayout->addWidget(m_button);
}

bool Widget::event(QEvent* event){
    switch(event->type()){
        case QEvent::FocusIn:
            focusIn(static_cast<QFocusEvent*>(event));
            return true;
            break;
        default:
            break;
    }
    return QWidget::event(event);
}

void Widget::focusIn(QFocusEvent* event){
    qDebug() << "Widget::focusIn";
    emit focusReceived();
    QWidget::focusInEvent(event);
}

Итак, когдапри навигации по TableWidget вы ожидаете, что выделенные ячейки будут перемещаться с помощью курсора и «временно» придадут мягкий фокус виджету для событий клавиатуры.Это происходит ТОЛЬКО на объекте Button.Объект Button будет правильно печатать то, что в функции focusIn было отключено, когда выбранная ячейка содержит его.Однако можно ожидать, что такое же поведение будет происходить и с Widget, поскольку он добавляется точно так же с точно таким же кодом, только с Button, встроенным в его QHBoxLayout.Как только вы переходите к Button или Widget, клавиатура для TableWidget кажется сломанной, и нажатия клавиш не пересылаются с Widget на его дочерний Button, даже если я должен был установить setFocusProxy от Widget до m_button поля, которое точно такого же типа Button, которое может правильно получить KeyEvent.Я не совсем уверен, является ли это ошибкой или я исказил какое-то поведение.

1 Ответ

0 голосов
/ 18 июля 2019

Ну, оказывается, это поведение вызвано именно функцией QAbstractButton::keyPressEvent(QKeyEvent *e).Итак, когда кнопки добавляются в виджет ячейки TableWidget, я считаю, что таблица явно становится их родительским виджетом.Это имеет смысл для управления памятью, а что нет, хорошо.

Итак, QAbstractButtons имеет оператор case для ключей Qt::Key_Down и Qt::Key_Up, и они по существу проверяют, имеет ли родительский виджет тип QAbstractItemView*.Если они это делают, они вызывают функцию QAbstractButtonPrivate::move(int key).После запроса списка всех кнопок будет определена часть кнопки buttonGroup (что, я не уверен, мы можем контролировать), какая кнопка должна быть следующей.Это, очевидно, в QAbstractItemView, от которого наследуется QTableWidget, упорядочивает их в главном порядке столбцов, например, если в следующей строке или в следующем столбце есть кнопка, вместо нее будет выбран столбец в следующей строке, если Qt::Key_Down пойман.Это объясняет поведение, которое я получал.Почему они делают это, базовое поведение меня побеждает, потому что я нахожу очень странным, что я не могу изменить это поведение, так как это часть класса QAbstractButtonPrivate, который мы, очевидно, не можем контролировать.К счастью, это virtual, и мы можем переопределить поведение.

Таким образом, решение состоит в том, чтобы реализовать Button::keyPressEvent(QPressEvent *e) и переопределить в каждом случае Qt::Key_Down и Qt::Key_Up, чтобы испустить сигнал, который будет захваченTable и возврат.Не break и вызовите реализацию базового класса для любого из этих случаев.При генерации emit идентификатор, очень похожий на функцию QAbstractButtonPrivate::move(int key), вносит в таблицу и устанавливает выбранную ячейку / текущий элемент в слоте, который вы добавляете в Table, в качестве следующего элемента / виджета вправо / влевоили вверх / вниз, как и следовало ожидать при обычном поведении клавиш QTableWidgetItems.Похоже, это сработало, и я наконец смог сфокусировать кнопки, встроенные в виджет, с правильным поведением выбора.

...