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

Alles rund um die Programmierung mit Qt
Antworten
ska
Beiträge: 9
Registriert: 5. März 2010 16:48

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

Beitrag 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);
        }
    }
}
-ska
trendyAndy
Beiträge: 54
Registriert: 27. März 2009 15:01

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

Beitrag 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
ska
Beiträge: 9
Registriert: 5. März 2010 16:48

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

Beitrag 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);
}
-ska
ska
Beiträge: 9
Registriert: 5. März 2010 16:48

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

Beitrag 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);
}
-ska
Antworten