Seite 1 von 1

QTableView: auf nächste Zeile wechseln, bei Tab in Spalte 7

Verfasst: 15. April 2012 01:25
von ska
Hallo,

ich benutze Qt 4.7.0 und lasse mit QTableView ein QSqlRelationalTableModel anzeigen, wenn der Nutzer durch die Tabelle tabt, wird die nächste Zelle aktive, sie wird selektiert und die vorherige deselektiert. Das ist OK so.

Nun möchte ich, dass wenn der Nutzer in Spalte 7 die Tab-Taste drückt, zur 1. Spalte der nächsten Zeile gewechselt wird. Die Zellen jenseits der 7. Spalte sollen jedoch weiterhin bspw. durch die Maus aktiviert und auch selektiert werden können.

Folgende 2 Ansätze habe ich versucht, jedoch haben beide dasselbe Problem: Der Index geht auf die nächste Zeile, die ausgewählte Zelle jedoch bleibt in der alten Zeile. Beispiel: aktuell gewählte Zelle ist 4x7, die auch die einzige selektierte ist, Nutzer drückt Tab-taste, 5x1 wird die aktive Zelle, 4x8 jedoch die ausgewählte Zelle.

Wenn "logger("clear", true);" ausgeführt wird, ist keine Zelle selektiert, was IMHO besagt, dass ui->table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); funktioert; sobald der Signalhandler jedoch beendet wird, wird die Zelle in Spalte 8 wieder aktiviert.

Was kann ich tun, damit die Auswahl in der alten Zeile aufgehoben wird, oder besser nur die aktuelle Zelle ausgewählt ist?

Code: Alles auswählen

#if 1 // just use one connect() at a time
    connect(ui->table->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(selChanged(const QItemSelection&,const QItemSelection&)));
#else
    connect(ui->table->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)), this, SLOT(curChanged(const QModelIndex&,const QModelIndex&)));
#endif

Code: Alles auswählen

void db::curChanged(const QModelIndex &n, const QModelIndex &o)
{
    logger(QString("curChanged(): old=%1x%2 new=%3x%4").arg(o.row()).arg(o.column()).arg(n.row()).arg(n.column()));

    if(o.row() == n.row() && n.column() == 8 && o.column() == 7) {
        int r = 1 + n.row();
        int c = firstVisibleColumn();
        logger(QString("curChanged() set selection %1x%2").arg(r).arg(c));
        QModelIndex index = ui->table->model()->index(r, c);
        ui->table->selectionModel()->clear();
        ui->table->setCurrentIndex(index);
        ui->table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
        //logger("clear", true);
    }
}

Code: Alles auswählen

void db::selChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    if(!selected.isEmpty() && selected.size() == 1
      && !deselected.isEmpty() && deselected.size() == 1
      ) {
        QModelIndex o = deselected.indexes().first();
        QModelIndex n =   selected.indexes().first();

        //logger(QString("selChanged(): old=%1x%2 new=%3x%4").arg(o.row()).arg(o.column()).arg(n.row()).arg(n.column()));

        if(o.row() == n.row() && n.column() == 8 && o.column() == 7) {
            int r = 1 + n.row();
            int c = firstVisibleColumn();
            QModelIndex index = ui->table->model()->index(r, c);
            //ui->table->selectionModel()->clear();
            ui->table->setCurrentIndex(index);
            ui->table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
            //logger("clear", true);
        }
    }
}

Re: QTableView: auf nächste Zeile wechseln, bei Tab in Spalt

Verfasst: 16. April 2012 08:47
von trendyAndy
Moin ska,

für sowas eignet sich am besten ein ItemDelegate. Siehe "An Introduction to Model/View Programming" in der Qt Hilfe und hier im Forum hats auch super Beispiele.

Gruß
Andre

Re: QTableView: auf nächste Zeile wechseln, bei Tab in Spalt

Verfasst: 2. Mai 2012 23:42
von ska
Im Modus, wenn kein Editor aktiv ist, klappt folgendes (ItemDelegate ist nicht involviert)

Code: Alles auswählen

class wrapOnTab : public QObject
{
    Q_OBJECT

public:
    wrapOnTab(int col, QWidget *parent = 0) : QObject(parent) { column = col; }

protected:
    int column;
    bool eventFilter(QObject *obj, QEvent *event);
};

// wrap if in non-editing mode
bool wrapOnTab::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if(keyEvent->key() == Qt::Key_Tab) {
            //qDebug("Got TAB key");
            QTableView *t = static_cast<QTableView *>(obj);
            if(t) {
                QModelIndex i = t->currentIndex();
                //qDebug(QString("row x col = %1x%2").arg(i.row()).arg(i.column()).toStdString().c_str());
                if(i.column() == column) {
                    //qDebug(QString("wrap to = %1x%2").arg(i.row() + 1).arg(tblFirstVisibleColumn(t)).toStdString().c_str());
                    QModelIndex index = t->model()->index(i.row() + 1, tblFirstVisibleColumn(t));
                    t->setCurrentIndex(index);
                    return true;
                }
            }
        }
    }

   // standard event processing
   return QObject::eventFilter(obj, event);
}
Im Editier-Modus klappt das nicht, da habe ich folgendes probiert; hier steht bei "wrap to" das Richtige, doch der Table geht nach 1x1 zurück und wann immer ich anschließend in die Editier-Modus wechsle und TAB drücke, geht der Table in non-Editier-Modus nach Zelle 1x1.

Code: Alles auswählen

class itemDelegateTabNxtLine : public QStyledItemDelegate
{   Q_OBJECT
public:
    itemDelegateTabNxtLine(QTableView *table, QObject *parent = 0) : QStyledItemDelegate(parent) { t = table; }
protected:
    QTableView *t;
    bool eventFilter(QObject *editor, QEvent *event);
};

bool itemDelegateTabNxtLine::eventFilter(QObject *editor, QEvent *event) {
    //qDebug("itemDelegate::eventFilter()");
    if(event->type() == QEvent::KeyPress) {
         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
         //qDebug("eventFilter(): keypress");

         if(keyEvent->key() == Qt::Key_Tab) {
             qDebug("itemDelegate::eventFilter(): TAB key");
             qDebug(QString("eventFilter(): row = %1").arg(t->currentIndex().row()).toStdString().c_str());
             QModelIndex i = t->currentIndex();
             qDebug(QString("wrap to = %1x%2").arg(i.row() + 1).arg(tblFirstVisibleColumn(t)).toStdString().c_str());
             QModelIndex index = t->model()->index(i.row() + 1, tblFirstVisibleColumn(t));
             //emit commitData(static_cast<QWidget *>(editor));
             //emit closeEditor(static_cast<QWidget *>(editor));
             bool ret = QStyledItemDelegate::eventFilter(editor, event);
             t->setCurrentIndex(index);
             return ret;
          }
     }
    return QStyledItemDelegate::eventFilter(editor, event);
}

Re: QTableView: auf nächste Zeile wechseln, bei Tab in Spalt

Verfasst: 27. Mai 2012 16:55
von ska
Zur Info, ich habe jetzt folgenden Code im Projekt. Im non-editing-Modus funktioniert das zu meiner Zufriedenheit, während des Editierens auch, aber anschließend, d.h. wenn ich mit der Maus auf eine andere Zelle klicke, funktioniert Tab nicht, sondern das Programm geht wieder in den Nicht-Editier-Modus. Das scheint allerdings auch ein Qt-Problem zu sein, da ich dieses Verhalten manchmal auch sonst habe. Wählt man ein 2. Mal eine Zelle zum Editieren aus und drückt TAB, bleibt das Programm in Editier-Modus.

Code: Alles auswählen

// wrap if in non-editing mode
bool wrapOnTab::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if(keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) {
            //qDebug("Got TAB key");
            QTableView *t = static_cast<QTableView *>(obj);
            if(t) {
                QModelIndex i = t->currentIndex();
                //qDebug(QString("row x col = %1x%2").arg(i.row()).arg(i.column()).toStdString().c_str());
                if(i.column() == column) {
                    //qDebug(QString("wrap to = %1x%2").arg(i.row() + 1).arg(tblFirstVisibleColumn(t)).toStdString().c_str());
                    QModelIndex index = t->model()->index(i.row() + 1, tblFirstVisibleColumn(t));
                    t->setCurrentIndex(index);
                    return true;
                }
            }
        }
    }

   // standard event processing
   return QObject::eventFilter(obj, event);
}

bool itemDelegateTabNxtLine::eventFilter(QObject *editor, QEvent *event) {
    //qDebug("itemDelegate::eventFilter()");
    if(event->type() == QEvent::KeyPress) {
         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
         //qDebug("eventFilter(): keypress");

         if(keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == 0) {
             qDebug("itemDelegate::eventFilter(): TAB key");
             QTableView *t = qobject_cast<QTableView *>(this->parent());
             if(!t) {
                 qDebug("no QTableView as parent");
                 return false;
             }
             qDebug(QString("eventFilter(): row = %1").arg(t->currentIndex().row())/*.toStdString().c_str()*/);
             QSqlRelationalTableModel *model = qobject_cast<QSqlRelationalTableModel *>(t->model());
             if(!model) {
                 qDebug("no QSqlRelationalTableModel in table");
                 return false;
             }

             QModelIndex i = t->currentIndex();
             qDebug(QString("wrap to = %1x%2").arg(i.row() + 1).arg(tblFirstVisibleColumn(t))/*.toStdString().c_str()*/);
             QModelIndex index = t->model()->index(i.row() + 1, tblFirstVisibleColumn(t));

             enum QAbstractItemDelegate::EndEditHint editHint = QAbstractItemDelegate::NoHint;
             if(model->editStrategy() != QSqlTableModel::OnManualSubmit) {
                 editHint = QAbstractItemDelegate::SubmitModelCache;
                 qDebug("No Manual Submit -> editHint submit model cache");
             }
             closeEditor(static_cast<QWidget *>(editor), editHint);

             if(editHint == QAbstractItemDelegate::SubmitModelCache) {
                 qDebug("model->submitAll()");
                 (void)model->submitAll();
             }
             t->setCurrentIndex(index);
             t->edit(index);
             //qDebug("sleep");
             //sleep(2);
             return true;   // eat Tab
          }
     }
    return QStyledItemDelegate::eventFilter(editor, event);
}