PDA

View Full Version : QTableWidget + setCellWidget - controlling tab order



elmo
13th September 2009, 13:30
I've been trying for some time now to make QTableWidget behave the way I want it.
I have a few MyTableWidgets (which inherit from QTableWidget) on the one tab. I modified QTableWidget so it would display in the first (and as for now only) row QLineEdits (to be precise it displays my class inheriting from QLineEdit) using QTableWidget::setCellWidget().
Now I would like to traverse through all MyTableWidgets on this tab.
If I have only a one column (so it's also the only cell in MyTableWidget) when I press Tab it goes to the next MyTableWidget (and I've reimplemented focusInEvent so the first cell in this MyTableWidget gets the focus). When I press Tab again and again it loops through all cells in the only row in this MyTableWidget, but never leaves MyTableWidget and I don't know where to change this behavior.
So my question is how to change QTableWidget so that after pressing Tab in the last cell it will go to the next widget not first cell (and shift+tab in the first cell to go to the previous widget)?

From all I read it appears I should do something by reimplementing focusNextPrevChild( bool), but I just can't get it to work properly.

edit:
I think I found the solution:


bool MyTableWidget::focusNextPrevChild( bool next )
{
std::cout << "focusNextPrev " << qPrintable(objectName()) << std::endl;
if( next )
{
if( currentColumn() == m_model->columnCount() - 1 )
return QWidget::focusNextPrevChild(next);
return false;
}
else
{
if( currentColumn() == 0 )
return QWidget::focusNextPrevChild(next);
return false;
}


return QTableWidget::focusNextPrevChild(next);
}


void MyTableWidget::focusInEvent( QFocusEvent * e)
{
switch( e->reason() )
{
case Qt::BacktabFocusReason:
cellWidget(0, m_model->columns().count()-1)->setFocus();
break;
case Qt::TabFocusReason:
default:
cellWidget(0,0)->setFocus();
break;
}
}

As far as I checked it does work, though I'm not sure it's entirely correct.

elmo
16th September 2009, 00:39
So after some playing around it appears it doesn't work as it should.
I have:

main.cpp

#include <QGroupBox>
#include <QApplication>
#include <QHBoxLayout>
#include <QVBoxLayout>

#include <MyTable.h>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QWidget *window = new QWidget();
QVBoxLayout *layout = new QVBoxLayout();

{
QGroupBox * box = new QGroupBox(window);
QHBoxLayout * sublay = new QHBoxLayout();
sublay->addWidget( new MyTable(box));
box->setLayout(sublay);

layout->addWidget(box);
}

{ /* same as above */
QGroupBox * box = new QGroupBox(window);
QHBoxLayout * sublay = new QHBoxLayout();
sublay->addWidget( new MyTable(box));
box->setLayout(sublay);

layout->addWidget(box);
}

{ /* same as above */
QGroupBox * box = new QGroupBox(window);
QHBoxLayout * sublay = new QHBoxLayout();
sublay->addWidget( new MyTable(box));
box->setLayout(sublay);

layout->addWidget(box);
}

window->setLayout(layout);
window->show();

return app.exec();
}

MyTable.h

#include <QTableWidget>
#include <QFocusEvent>
#include <QEvent>
#include <QLineEdit>

#include <iostream>

class MyTable : public QTableWidget
{
Q_OBJECT

public:
MyTable( QWidget * parent = 0) : QTableWidget(parent)
{
setColumnCount(7);
setRowCount(1);

for(unsigned int i = 0; i < columnCount(); ++i)
{
setCellWidget(0, i, new QLineEdit(this));
}
}

bool focusNextPrevChild( bool next )
{
QWidget::focusNextPrevChild(next);
}
};

The code above is compilable.
To compile:

moc MyTable.h -o MyTable.cpp
g++ MyTable.cpp main.cpp `pkg-config QtGui --cflags --libs` -I . -o table



First question: shouldn't this work like this: when I press tab it goes to the next MyTable widget?
Because I thought that if I call directly QWidget::focusNextPrevChild(bool) it will treat MyTable as any other QWidget which can have focus, so it should navigate through all three MyTables not going into cells.

elmo
16th September 2009, 15:40
Another attempt:

MyTable.h:
#include <QTableWidget>
#include <QFocusEvent>
#include <QEvent>
#include <QLineEdit>

#include <iostream>

class MyTable : public QTableWidget
{
Q_OBJECT

public:
MyTable( QWidget * parent = 0) : QTableWidget(parent)
{
setColumnCount(7);
setRowCount(1);

for(unsigned int i = 0; i < columnCount(); ++i)
{
QWidget * e = new QLineEdit(this);
setCellWidget(0, i, e);
e->setFocusPolicy( Qt::ClickFocus );
}
}

bool event( QEvent * e )
{
switch( e->type() )
{
case QEvent::KeyPress:
e->accept();
QKeyEvent *ke = (QKeyEvent *) e;
QObject *p = parent();
bool res = true;

switch( ke->key() )
{
case Qt::Key_Backtab:
res = focusNextPrevChild(false);
break;
case Qt::Key_Tab:
res = focusNextPrevChild(true);
break;
}
return res;
}

return QTableWidget::event(e);
}

void focusInEvent( QFocusEvent *e )
{
switch( e->reason() )
{
case Qt::BacktabFocusReason:
setCurrentCell(0, columnCount()-1);
break;
case Qt::TabFocusReason:
default:
setCurrentCell(0,0);
break;
}
}

bool focusNextPrevChild( bool next )
{
if( next )
{
if( currentColumn() < columnCount() - 1 )
{
setCurrentCell( 0, currentColumn() + 1);
return true;
}
}
else if( currentColumn() > 0 )
{
setCurrentCell( 0, currentColumn() - 1);
return true;
}

return false;
}

};


I don't know why this works only one way: when pressing tab it loops as it should. But when I press Shift+Tab ( = Key_Backtab) it loops inside one MyTable. Why does it behave like that?

faldzip
16th September 2009, 16:52
ANd why do you need QTableWidget? If you want to place several QLineEdits in a row use QHBoxLayout. In more than one row use QGridLayout.

elmo
16th September 2009, 17:14
ANd why do you need QTableWidget? If you want to place several QLineEdits in a row use QHBoxLayout. In more than one row use QGridLayout.
1. I would like it to look like a grid ( possibly with vertical header, but horizontal header is a must) and user has to have the ability to resize columns when "grabbing" in between two header cells.
2. I would like to know what's going on and how to control tab traversal order (as I can't find anything in the docs and I can't google anything useful).

faldzip
16th September 2009, 21:24
QLabels with QSplitters and QLineEdits could be your header and a whole view. You can even make it in Designer when you can easily set the tab order

elmo
17th September 2009, 00:27
QLabels with QSplitters and QLineEdits could be your header and a whole view. You can even make it in Designer when you can easily set the tab order
Hm... QSpliters. Haven't noticed that. I think I will go with that idea, but still I would like to know how the heck can I control tab traversal order.

elmo
18th September 2009, 11:59
still I would like to know how the heck can I control tab traversal order.
Bump. Really noone has a clue on how to control tab order with QTableWidget/View?